<?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[Elasticsearch Labs]]></title>
        <description><![CDATA[Artigos e tutoriais da equipe de pesquisa da Elastic]]></description>
        <link>https://www.elastic.co/search-labs/pt</link>
        <image>
            <url>https://www.elastic.co/search-labs/favicon.svg</url>
            <title>Elasticsearch Labs</title>
            <link>https://www.elastic.co/search-labs/pt</link>
        </image>
        <generator>Elasticsearch Labs</generator>
        <lastBuildDate>Thu, 23 Apr 2026 16:31:18 GMT</lastBuildDate>
        <atom:link href="https://www.elastic.co/search-labs/pt/rss/feed" rel="self" type="application/rss+xml"/>
        <copyright><![CDATA[© 2026. Elasticsearch B.V. Todos os direitos reservados]]></copyright>
        <language><![CDATA[pt]]></language>
        <item>
            <title><![CDATA[Jina embeddings v3 agora disponível no Gemini Enterprise Agent Platform Model Garden]]></title>
            <description><![CDATA[O modelo de busca Jina, jina-embeddings-v3, agora é autoimplantável na plataforma Gemini Enterprise Agent Platform Model Garden, com mais novidades por vir. Execute jina-embeddings-v3 em uma única GPU L4 dentro da sua própria VPC.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/jina-embeddings-v3-gemini-enterprise-model-garden</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/jina-embeddings-v3-gemini-enterprise-model-garden</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sa Zhang]]></dc:creator>
            <pubDate>Wed, 22 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42f72633f1e5453dbfd47730b5f776429f9f633e-721x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Hoje estamos lançando o <code>jina-embeddings-v3</code>, o primeiro modelo de base de busca Jina disponível no <a href="https://console.cloud.google.com/vertex-ai/publishers/jinaai/model-garden/jina-embeddings-v3">Gemini Enterprise Agent Platform Model</a><a href="https://console.cloud.google.com/vertex-ai/publishers/jinaai/model-garden/jina-embeddings-v3"> Garden</a> como um modelo de parceiro autoimplantável. <em>A autoimplantação</em> significa que o modelo é executado em instâncias de GPU dentro do seu projeto do Google Cloud e da sua Virtual Private Cloud (VPC). Sem chamadas de API externas, sem medição por token, sem limites de taxa.</p><p>Com essa integração, os usuários do Elasticsearch ganham uma nova opção de implantação que mantém os dados dentro do perímetro de segurança, entrega custos de infraestrutura previsíveis e roda de forma nativa no Google Cloud. Ao mesmo tempo, o ecossistema mais amplo do Google Cloud ganha acesso aos modelos de busca e recuperação de última geração desenvolvidos especificamente pelo Jina.</p><p>Esta é a primeira etapa de uma implementação mais ampla. Junto com os modelos que virão a seguir, a linha formará uma pilha completa de recuperação: incorpore seus dados, incorpore consultas, recupere e reclassifique candidatos, e estenda a busca para imagens com embeddings multimodais, tudo na infraestrutura que você controla. Você pode começar hoje com <code>jina-embeddings-v3</code>, o modelo que já alimenta pipelines de busca em produção em todo o ecossistema Elasticsearch via Elastic Inference Service (EIS).</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Modelo</td><td style="border: 1px solid #ccc; padding: 4px;">Tipo</td><td style="border: 1px solid #ccc; padding: 4px;">Parâmetros</td><td style="border: 1px solid #ccc; padding: 4px;">Principal recurso</td><td style="border: 1px solid #ccc; padding: 4px;">Status do Model Garden</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`jina-embeddings-v3`</td><td style="border: 1px solid #ccc; padding: 4px;">Embedding de texto</td><td style="border: 1px solid #ccc; padding: 4px;">572 milhões</td><td style="border: 1px solid #ccc; padding: 4px;">Ferramenta multilíngue comprovada, contexto 8K, saída de 1024 dimensões, truncável para 32</td><td style="border: 1px solid #ccc; padding: 4px;">Disponível agora</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`jina-embeddings-v5-text-small`</td><td style="border: 1px solid #ccc; padding: 4px;">Embedding de texto</td><td style="border: 1px solid #ccc; padding: 4px;">677M</td><td style="border: 1px solid #ccc; padding: 4px;">Multilíngue sub-1B de última geração, contexto de 32K, saída de 1024 dimensões, truncável para 32</td><td style="border: 1px solid #ccc; padding: 4px;">Em breve</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`jina-embeddings-v5-text-nano`</td><td style="border: 1px solid #ccc; padding: 4px;">Embedding de texto</td><td style="border: 1px solid #ccc; padding: 4px;">239 milhões</td><td style="border: 1px solid #ccc; padding: 4px;">O melhor da categoria com menos de 500 milhões de parâmetros, contexto de 8K, saída de 768 dimensões, truncável até 32</td><td style="border: 1px solid #ccc; padding: 4px;">Em breve</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`jina-reranker-v3`</td><td style="border: 1px solid #ccc; padding: 4px;">Reclassificador</td><td style="border: 1px solid #ccc; padding: 4px;">600 milhões</td><td style="border: 1px solid #ccc; padding: 4px;">Reclassificador listwise, contexto de 131 mil, até 64 documentos</td><td style="border: 1px solid #ccc; padding: 4px;">Em breve</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`jina-clip-v2`</td><td style="border: 1px solid #ccc; padding: 4px;">Embedding multimodal</td><td style="border: 1px solid #ccc; padding: 4px;">900M</td><td style="border: 1px solid #ccc; padding: 4px;">Texto e imagem em espaço compartilhado, 89 idiomas e contexto de texto de 8K, imagens 512 × 512</td><td style="border: 1px solid #ccc; padding: 4px;">Em breve</td>
                    </tr>
              </tbody>
            </table>
          <p>Cada modelo roda em uma única placa NVIDIA L4 (24 GB), a camada de GPU mais econômica do Google Cloud. A maioria dos outros modelos de incorporação no Google Cloud Model Garden exige um A100 de 80 GB ou H100, aproximadamente três vezes o custo da instância por hora antes mesmo de você começar a contar os tokens.</p><p>Não é necessária licença comercial adicional quando implantada via Vertex AI.</p><h2><strong>Por que o Model Garden?</strong></h2><p>Por que implantar pelo Model Garden em vez de usar uma API? Tudo se resume a três coisas: controle, custo e contexto.</p><h3>Seus dados nunca saem do ambiente seguro</h3><p>O principal atrativo para a maioria dos desenvolvedores é a arquitetura de autoimplantação. Quando você implanta um modelo Jina pelo Model Garden, os pesos são executados em instâncias de GPU dentro do seu próprio projeto Google Cloud e da sua própria VPC. Isso é um divisor de águas para qualquer pessoa que trabalhe em setores com preocupações com segurança de dados, como finanças ou saúde. Como não há chamadas externas de API, seus dados sensíveis permanecem dentro do seu perímetro de segurança.</p><h3>Redimensionamento com previsão</h3><p>Em vez de pagar toda vez que você incorpora uma frase ou reclassifica um documento, você paga um custo fixo por hora de instância. E como todo modelo Jina pode rodar em uma única NVIDIA L4, a camada de GPU mais acessível do Google Cloud, a barreira de entrada é baixa. Seja processando mil solicitações ou um bilhão, sua conta de infraestrutura permanece previsível. Essa é uma configuração que realmente recompensa você por aumentar seu tráfego, em vez de te taxar por isso.</p><h3>Tudo sob o mesmo teto</h3><p>Se seus dados já estão no Elasticsearch na Google Cloud, BigQuery ou Cloud Storage, faz sentido manter seus mecanismos de inferência próximos. Ao serem implementados por meio do Model Garden, os modelos da base de pesquisa Jina herdam todos os recursos corporativos que você já utiliza: gerenciamento de identidade e acesso (IAM) para controle de acesso, faturamento unificado na fatura existente do Google Cloud e a capacidade de integração com o Vertex AI Pipelines para fluxos de trabalho de operações de aprendizado de máquina (MLOps).</p><p>Embora a API Jina AI Cloud e o Elastic Cloud ofereçam o caminho mais rápido para picos de tráfego ou fluxos de trabalho de busca existentes, o Model Garden é ideal para aplicações corporativas que exigem segurança de dados rigorosa e custos previsíveis em grande escala. A Elastic quer encontrar você onde você estiver.</p><h2><strong>Modelos Jina AI</strong></h2><h3><strong>jina-embeddings-v3</strong></h3><p>Nosso modelo comprovado de embedding multilíngue com 572 milhões de parâmetros e contexto de token 8K. Pontuação 65,5 no Massive Text Embedding Benchmark (MTEB) em inglês. Compatível com cinco adaptadores LoRA (Low-Rank Adaptation) específicos para tarefas (consulta/passagem de recuperação, correspondência de texto, classificação, agrupamento) e truncamento Matryoshka de 1024 para 64 dimensões. Já amplamente adotado em todo o ecossistema Elasticsearch via EIS.</p><p>Estamos priorizando a v3 porque muitos sistemas de produção já dependem dela. Se você está migrando um pipeline baseado em v3 para o Google Cloud, agora pode executar o mesmo modelo de forma nativa sem alterar as dimensões de embedding nem reindexar.</p><h3><strong>jina-embeddings-v5-text (pequeno e nano)</strong></h3><p>Nossos modelos de incorporação de texto de quinta geração, lançados em fevereiro de 2026, alcançam desempenho de alto nível, competindo com modelos muitas vezes maiores.</p><p><code>v5-text-small</code> (677 milhões) pontua 67,0 no conjunto de benchmarks Multilingual MTEB (MMTEB), abrangendo 131 tarefas de nove tipos, e 71,7 no benchmark MTEB em inglês. É o modelo de incorporação multilíngue sub-1B mais forte no MTEB Leaderboard.</p><p><code>v5-text-nano</code> (239 milhões) tem 65,5 pontos no MMTEB. Nenhum outro modelo com menos de 500 milhões de parâmetros atinge esse nível. Com menos da metade do tamanho da maioria dos modelos comparáveis, é a escolha natural para implantações sensíveis à latência.</p><p>Ambos os modelos oferecem suporte:</p><ul><li><strong>Quatro adaptadores LoRA específicos para tarefas:</strong> recuperação, correspondência de texto, classificação e clustering. Selecionar um adaptador apropriado por meio do parâmetro <code>task</code> no momento da inferência.</li><li><strong>Truncamento da dimensão matrioshka:</strong> reduza as dimensões de embedding de 1024 (ou 768 para nano) para 32. A perda de qualidade é mínima em truncamento moderado (por exemplo, 256 dimensões). Ao reduzir as dimensões pela metade, você reduz o armazenamento pela metade.</li><li><strong>Quantização binária:</strong> comprima embeddings de 1024 dimensões de 2KB para 128 bytes com binarização. O treinamento especial faz com que esta compressão tenha perdas mínimas.</li><li><strong>Multilíngue: </strong>119 idiomas (pequeno) e 93 (nano).</li></ul><h3><strong>jina-reranker-v3</strong></h3><p>Um reclassificador de listas multilíngue com 0,6 bilhões de parâmetros, construído usando uma arquitetura <em>de interação última, mas não tardia</em>. A consulta e até 64 correspondências de candidatos são inseridas em uma única janela de contexto de 131 mil tokens, e o modelo realiza uma comparação cruzada de documentos antes da pontuação. O Jina Reranker v3 alcança 61,94 nDCG@10 no BEIR, superando o modelo por ser 6× menor em tamanho. Isso é fundamentalmente diferente dos reclassificadores pontuais, que pontuam cada documento isoladamente, produzindo melhores resultados, especialmente para recuperação de trechos a partir de documentos isolados.</p><h3><strong>jina-clip-v2</strong></h3><p>Um modelo de incorporação multimodal e multilíngue de 0,9B que mapeia texto e imagens em um espaço compartilhado de 1024 dimensões. Ele é compatível com:</p><ul><li><strong>89 idiomas</strong> para recuperação de texto-imagem.</li><li><strong>Resolução de imagem 512×512.</strong></li><li><strong>Entrada de texto com token de 8 mil.</strong></li><li><strong>Truncamento de Matryoshka</strong> de 1024 para 64 dimensões para ambas as modalidades.</li></ul><p>Altamente competitivo em benchmarks de imagem para texto, incluindo tarefas multilíngues.</p><h2><strong>Para começar</strong></h2><p>Jina Embeddings v3 está disponível no Model Garden hoje. Veja como fazê-lo funcionar.</p><p>Você precisa de um projeto do Google Cloud com a API Vertex AI habilitada e cota de GPU suficiente para pelo menos uma instância g2-standard-8 (NVIDIA L4). Se você é novo no Google Cloud, <a href="https://cloud.google.com/vertex-ai/docs/start/cloud-environment">comece pelo guia de configuração.</a></p><p><a href="https://console.cloud.google.com/vertex-ai/publishers/jinaai/model-garden/jina-embeddings-v3">A página Model Garden para Jina Embeddings v3</a> guia você pelo fluxo completo: faça upload do modelo, crie um endpoint, escolha o tipo de máquina e implante. Abra-o em seu próprio projeto e siga as etapas guiadas. Máquinas A100 e H100 também estão disponíveis onde a região e a cota permitem, mas L4 é tudo que você precisa para começar.</p><p>Desde o clique até a primeira incorporação, todo o processo leva alguns minutos.</p><h2><strong>O que vem depois</strong></h2><p>Jina Embeddings v3 é o ponto de partida. Nas próximas semanas, traremos o restante do stack de recuperação Jina para o Model Garden: embeddings de texto v5 (pequeno e nano), jina-reranker-v3 e jina-clip-v2 para busca multimodal. Tudo será executado em uma única GPU L4 com o mesmo modelo de autoimplantação.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elastic Security, Observability e Search agora oferecem UI interativa nas ferramentas de IA]]></title>
            <description><![CDATA[A Elastic é a primeira fornecedora de segurança a enviar uma interface de usuário interativa no AI Conversations, criada com base no padrão MCP App que a Anthropic e a OpenAI criaram em parceria. Ela será lançada juntamente com outros dois Elastic MCP Apps para Observability e Search.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/mcp-apps-elastic</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/mcp-apps-elastic</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[David Elgut, Graham Hudgins, Jesse Miller]]></dc:creator>
            <pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/55cea79fd5c554656e8605525a61d4ad3ae0ee1b-2048x1143.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Agentes de IA são tão bons quanto as interfaces que eles retornam. Um resultado de ferramenta somente em texto achata tudo que é naturalmente visual ou interativo: um dashboard, uma lista de alertas, um gráfico de investigação, um rastreamento distribuído. A conversa se torna um lugar onde você pergunta e o agente responde, mas assim que o trabalho fica mais complexo, você vai para uma aba separada, um produto separado, uma barreira de autenticação separada.</p><p><a href="https://modelcontextprotocol.io/extensions/apps/overview">Os MCP Apps</a> mudam a forma dessa resposta. Uma ferramenta agora pode retornar uma interface interativa junto com o resumo em texto, e o host (Claude Desktop, Claude.ai, VS Code Copilot, Cursor) o renderiza na conversa. O modelo mantém o texto compacto para raciocínio. O humano recebe uma interface ao vivo e clicável bem ao lado do chat.</p><p>Três propriedades tornam esta integração diferente de “um webhook que retorna uma URL”:</p><ul><li><strong>Preservação de contexto.</strong> A UI fica dentro da conversa. Sem alternância de abas, sem transferências.</li><li><strong>Fluxo de dados bidirecional.</strong> A UI pode chamar ferramentas no servidor MCP para dados novos e o host pode enviar novos resultados do agente de volta para a UI. Sem camada de API separada ou infraestrutura de autenticação.</li><li><strong>Limite de confiança sandbox.</strong> Os MCP Apps são executados em um iframe controlado pelo host. Eles não podem acessar a página principal, ler cookies nem sair do container.</li></ul><p>As operações de segurança são executadas com base em triagem, gráficos de investigação e descoberta de ataques, onde um agente de IA correlaciona centenas de alertas em algumas cadeias de ataque. A observabilidade significa rastreamento distribuído e análises detalhadas de séries temporais. Criar no Kibana significa ter uma grade de dashboards. Ao transformar qualquer uma dessas informações em texto simples, você perde o que a torna útil. Criamos MCP Apps para os três e estamos disponibilizando-os como open source juntos, para que a mesma conversa possa passar de uma fila de triagem para um gráfico de dependência e um dashboard em tempo real sem sair do chat.</p><p>Cada um dos três apps de referência é um servidor MCP que oferece muitas visões interativas, não um conjunto de produtos separados. Só o app de segurança exibe seis dashboards que compartilham o mesmo shell de servidor, o mesmo modelo de visibilidade de ferramentas e a mesma ponte host. O padrão é pequeno; a área de superfície é onde o valor se compõe.</p><h2>Elastic Security MCP App</h2><h3>Por que isso é importante para o SOC</h3><p>Quando um agente diz a um analista do SOC: &quot;<em>Há 47 alertas no host-314, aqui está um resumo</em>&quot;, ele não fez nenhum trabalho. Ele apenas aponta para onde o trabalho começa. O trabalho real está na lista de alertas, na árvore de processos, no gráfico de investigação e no arquivo do caso. Você não pode fazer isso a partir de um parágrafo de texto.</p><p>O MCP App de segurança retorna o próprio fluxo de trabalho. O analista solicita informações ao agente, e o agente retorna um dashboard interativo no chat, onde o analista pode explorar alertas, executar buscas por ameaças, correlacionar cadeias de ataque e abrir chamados, tudo sem perder o fio da conversa. E como todas as descobertas, consultas e casos retornam ao Elasticsearch, a mesma investigação está à espera no Kibana, onde o analista pode retomar após o encerramento da conversa.</p><h3>Seis dashboards interativos</h3><p>O <a href="https://github.com/elastic/example-mcp-app-security">Elastic Security MCP App</a> inclui seis elementos interativos, um para cada fluxo de trabalho principal do SOC. Cada um é uma UI React que renderiza quando o agente chama a ferramenta correspondente:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ferramenta</td><td style="border: 1px solid #ccc; padding: 4px;">O que você faz</td><td style="border: 1px solid #ccc; padding: 4px;">UI interativa</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Triagem de alertas</td><td style="border: 1px solid #ccc; padding: 4px;">Busque, filtre e classifique alertas de segurança</td><td style="border: 1px solid #ccc; padding: 4px;">Agrupamento de gravidade, cartões de decisão de IA, árvore de processos e eventos de rede</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Descoberta de ataque</td><td style="border: 1px solid #ccc; padding: 4px;">Análise de cadeia de ataque correlacionada com IA e geração sob demanda</td><td style="border: 1px solid #ccc; padding: 4px;">Relatórios narrativos de ataque com pontuação de confiabilidade, risco de entidade e mapeamento MITRE</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Gerenciamento de casos</td><td style="border: 1px solid #ccc; padding: 4px;">Crie, busque e gerencie casos de investigação</td><td style="border: 1px solid #ccc; padding: 4px;">Lista de casos com alertas, observáveis, abas de comentários e ações de IA</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Regras de detecção</td><td style="border: 1px solid #ccc; padding: 4px;">Navegue, ajuste e gerencie regras de detecção</td><td style="border: 1px solid #ccc; padding: 4px;">Navegador de regras com busca KQL, validação de consultas, análise de regras ruidosas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Caça a ameaças</td><td style="border: 1px solid #ccc; padding: 4px;">Bancada de trabalho ES|QL com investigação de entidades</td><td style="border: 1px solid #ccc; padding: 4px;">Editor de consultas, entidades clicáveis e gráfico de investigação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dados de amostra</td><td style="border: 1px solid #ccc; padding: 4px;">Gerar eventos de segurança do ECS para cenários de ataque comuns</td><td style="border: 1px solid #ccc; padding: 4px;">Seletor de cenários com quatro cadeias de ataque predefinidas</td>
                    </tr>
              </tbody>
            </table>
          <p>Cada ferramenta retorna um resumo em texto compacto sobre o qual o modelo pode raciocinar, junto com a UI que o analista usa. A UI também pode obter dados novos nos bastidores através da ponte de host MCP. O modelo completo da ferramenta e a API de ponte estão disponíveis no <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/architecture.md">documento de arquitetura do repositório</a>.</p><p>O app também é fornecido com <a href="https://github.com/elastic/example-mcp-app-security/tree/main/skills">habilidades do Claude Desktop</a>, <code>SKILL.md</code> arquivos que ensinam ao agente quando e como usar cada ferramenta. Faça o download de zips de habilidades pré-criadas da <a href="https://github.com/elastic/example-mcp-app-security/releases/latest">versão mais recente</a>.</p><h3>Do alerta ao caso</h3><p>Quatro habilidades abrangem o circuito do núcleo do SOC. Cada um capta um prompt, chama uma ferramenta e retorna um dashboard interativo junto com um resumo em texto que o modelo argumenta. O dia de um analista geralmente começa com uma fila de alertas.</p><p><strong>Alertas de triagem. </strong>Peça ao agente para priorizar por host, regra, usuário ou intervalo de tempo. Peça ao agente para priorizar por host, regra, usuário ou intervalo de tempo. A habilidade de Triagem de Alertas retorna um painel de vereditos de IA acima da lista de alertas brutos, com um veredito para cada regra de detecção, classificando a atividade dessa regra como benigna, suspeita ou maliciosa, cada um com uma pontuação de confiança e uma ação recomendada. Clique em qualquer alerta para abrir uma visualização detalhada com uma árvore de processos, eventos de rede, alertas relacionados e tags MITRE ATT&amp;CK. Não precisa alternar o contexto entre conversas de IA e seu dashboard de alertas dentro do Kibana, tudo acontece em tempo real dentro da sua conversa.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b0239ea916a828ee0bfd0cc046af17330568fe09-2048x1313.png" style="width: 100%;" /><p><strong>Procure ameaças. </strong>Peça ao agente para pesquisar nos seus índices. A funcionalidade Caça a ameaças retorna um workbench ES|QL com a consulta pré-preenchida e executada automaticamente, com cada entidade nos resultados clicável para detalhamento. O modelo escreve uma breve leitura abaixo da tabela: o que é incomum, o que está conectado, o que vale a pena conferir mais de perto. Em seguida, oferece a próxima opção: aprofundar-se na busca por ameaças ou iniciar uma nova habilidade no aplicativo MCP que complemente o trabalho realizado até o momento. O que combina muito bem com isso é o lançamento de uma descoberta de ataque para ter mais contexto sobre os alertas com os quais você se aprofundou e as ameaças que caçou até o momento. O que conecta tudo isso muito bem é o lançamento de um Attack Discovery para ter mais contexto sobre os alertas que você explorou e as ameaças que investigou até agora.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9f0bde828bcedb24e2b45995cfa306f03e57d300-2048x1313.png" style="width: 100%;" /><p><strong>Execute a Descoberta de ataques.</strong> A habilidade Descoberta de Ataques ativa a API de descoberta de ataques e retorna uma lista ranqueada de descobertas. Cada descoberta é um conjunto de alertas relacionados agrupados em uma cadeia de ataque, com táticas MITRE, uma pontuação de risco, um rótulo de confiança e os hosts e usuários afetados apresentados de forma clara. O resumo do agente aparece abaixo das descobertas na mesma ordem de classificação, e a conversa agora contém tudo o que é necessário para agir: consultas de busca, decisões de triagem, cadeias correlacionadas, tudo preparado para a próxima etapa.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e52af22dbaa070192170280d519dfb219ad807f5-2048x1313.png" style="width: 100%;" /><p><strong>Abra casos sem sair do chat.</strong> Aprove resultados em lote ou solicite ao agente que abra casos para alertas específicos. A habilidade de Gerenciamento de Casos cria um caso para cada descoberta aprovada (alertas de origem anexados, táticas MITRE herdadas da cadeia de ataque) e exibe a lista de casos ativos diretamente no aplicativo. Clique em um caso para ver a visualização detalhada, que inclui uma linha de botões de ação de IA: resumir caso, sugerir próximas etapas, extrair IOCs e gerar linha do tempo. Cada um deixa um prompt estruturado de volta no chat, para que o agente possa pegar o contexto do caso sem precisar de uma reintrodução. O resumo do agente está abaixo da lista de casos e abrange toda a fila de IR, incluindo os casos recém-abertos e as descobertas anteriores que ainda precisam de uma análise.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2057310b450c2917c5434c713f2b34732067429a-2048x1283.png" style="width: 100%;" /><p>Cada etapa deste passo a passo executa o mesmo ciclo: uma solicitação é recebida, a habilidade a processa, a ferramenta retorna um resumo de texto conciso para o modelo analisar, juntamente com uma UI que o analista usa. Ao combinar as habilidades, elas se integram em um fluxo SOC completo: busca, triagem, correlação, abertura de casos e direcionamento para a próxima etapa, tudo com o modelo carregando o contexto da sessão em cada passo. Invoque qualquer uma delas individualmente e ela ainda exibirá o dashboard completo, apontando para qualquer parte dos seus dados que você especificar. De qualquer forma, o trabalho se acumula dentro da conversa; sem troca de abas, sem copiar e colar, sem repasse de informações.</p><p>Duas outras funcionalidades complementam o app: um navegador de regras de detecção para ajustar regras ruidosas e um gerador de dados de amostra para simular eventos ECS realistas em um cluster novo. Um post de acompanhamento irá aprofundar todos os seis: gráfico de investigação, canvas de ataque e fluxo e guia de ponta a ponta.<br/></p><blockquote><em>&quot;O MCP App do Elastic Security faz a ponte entre a detecção automatizada e a caça manual a ameaças. Ao levar nossos dados de segurança diretamente para uma única interface no Claude Desktop, identificamos ameaças “silenciosas” em menos de uma hora, riscos que não acionavam alertas padrão, mas exigiam ação imediata. É um multiplicador de força para nossos analistas&quot;. Mandy Andress: diretora de segurança da informação (CISO), Elastic.</em></blockquote><h3>Como funciona</h3><p>Cada MCP App é um pequeno servidor Node.js cujas ferramentas retornam tanto um resumo em texto compacto do modelo quanto uma UI React que o host renderiza diretamente no aplicativo. Como ele foi desenvolvido com base na especificação aberta do MCP App, o mesmo servidor é executado em qualquer host compatível, consulte <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/architecture.md">o documento de arquitetura do repositório</a> para ver o projeto completo.</p><h3>Experimente</h3><p>Requer Elasticsearch 9.x com Security ativado, além do Kibana para casos, regras e Descoberta de Ataques. O caminho mais rápido é o pacote <code>.mcpb</code> com um clique da <a href="https://github.com/elastic/example-mcp-app-security/releases/latest">versão mais recente</a>, clique duas vezes no Claude Desktop, e será solicitado que você insira sua URL e chave API do Elasticsearch. Os guias de configuração para <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/setup-cursor.md">Cursor</a>, <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/setup-vscode.md">VS Code</a>, <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/setup-claude-code.md">Claude Code</a>, <a href="https://github.com/elastic/example-mcp-app-security/blob/main/docs/setup-claude-ai.md">Claude.ai</a> e compilação a partir do código-fonte estão no <a href="https://github.com/elastic/example-mcp-app-security">repositório</a>.</p><a href="https://videos.elastic.co/watch/voDxV9RWWpY9HeBYTWTS93">Watch video on Vidyard</a><h2>Elastic Search MCP App: dashboards criados a partir de conversas</h2><p>Todo usuário do Kibana conhece o desvio do dashboard: interrompa o que está fazendo, abra o Kibana, escolha um índice, escolha campos, escolha uma visualização, ajuste e salve. São cinco mudanças de contexto antes de um único gráfico aparecer na tela.</p><p>O novo app de referência <a href="https://github.com/elastic/example-mcp-dashbuilder">example-mcp-dashbuilder</a> integra isso em um prompt. Peça ao agente para &quot;<strong>criar um dashboard com métricas de receita, tendências de pedidos e detalhamento de categorias</strong>&quot; e o dashboard voltará para a conversa sem a necessidade de alternar entre guias.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b096b493726488f53040b08b5c8bc53ca0e44f9d-2048x1182.png" style="width: 100%;" /><p>Por trás desse comando, o agente explora seus dados do Elasticsearch via ES|QL e seleciona tipos de gráficos adequados aos dados: barras para comparações, linhas para tendências, cards de métricas para KPIs e mapas de calor para padrões bidimensionais. Ele organiza os painéis na grade de 48 colunas do Kibana usando o tema Elastic UI Borealis, e o resultado é totalmente interativo: você pode arrastar, redimensionar e agrupar painéis em seções recolhíveis diretamente no chat. Quando o dashboard estiver configurado corretamente, uma única chamada de ferramenta o exporta para o Kibana, preservando as consultas ES|QL e as cores personalizadas. Você também pode importar dashboards existentes do Kibana de volta para o chat para editar com a ajuda de IA.</p><p>O princípio é o mesmo por trás do app Security: quando o artefato é o produto, retorná-lo dentro da conversa fecha o ciclo entre descrever o que você quer e vê-lo.</p><p>Sob o capô, ele segue o mesmo padrão do MCP App. Um servidor Node.js registra uma ferramenta <code>view_dashboard</code> voltada para o modelo junto com um conjunto de ferramentas exclusivas de app que a UI chama diretamente (busca de dados, persistência do layout, detecção de campo de tempo, exportação/importação). A visualização do dashboard em si é um único arquivo HTML autônomo incluído com <code>vite-plugin-singlefile</code> e oferecido como recurso do MCP App. Os desenvolvedores que criam um fork do repositório têm o mesmo shell de servidor e a mesma ponte de host que veem no aplicativo de segurança, apontando para uma tarefa diferente. O README example-mcp-dashbuilder tem a arquitetura completa e a referência do tipo de gráfico.</p><a href="https://videos.elastic.co/watch/wep6PxPiEDyyvsYTKq6v5B">Watch video on Vidyard</a><h2>Elastic Observability MCP App</h2><p>O terceiro app de referência, <a href="https://github.com/elastic/example-mcp-app-observability">Elastic Observability MCP App</a>, aborda a versão SRE do problema de mesmo formato. Quando algo interrompe a produção, a resposta que o engenheiro de plantão precisa não é um gráfico, mas sim um diagnóstico feito a partir das métricas K8, da topologia do APM, das anomalias de ML e da avaliação de risco. A forma da resposta é uma história causal: o que falhou, por quê, o que depende disso e o que fazer a seguir.</p><p>Seis ferramentas que suportam o fluxo de trabalho de investigação de observabilidade</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ferramenta</td><td style="border: 1px solid #ccc; padding: 4px;">Dependência</td><td style="border: 1px solid #ccc; padding: 4px;">O que ele faz?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">observe</td><td style="border: 1px solid #ccc; padding: 4px;">Universal</td><td style="border: 1px solid #ccc; padding: 4px;">Primitivo transitório de acesso a anomalias ES|QL + ML, execute uma consulta única, colete uma amostra ao vivo de uma métrica ou aguarde até que um limite ou anomalia seja acionado.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">gerenciar alertas</td><td style="border: 1px solid #ccc; padding: 4px;">Universal</td><td style="border: 1px solid #ccc; padding: 4px;">Crie, liste, tenha e exclua regras de alerta personalizadas do Kibana. Omita a URL do Kibana para executar somente leitura.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">anomalias de ML</td><td style="border: 1px solid #ccc; padding: 4px;">Trabalhos de ML</td><td style="border: 1px solid #ccc; padding: 4px;">Consulte os registros de anomalias em ML e abra uma visualização explicativa de anomalias integrada.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">apm-health-summary</td><td style="border: 1px solid #ccc; padding: 4px;">Elastic APM</td><td style="border: 1px solid #ccc; padding: 4px;">Rollup consolidado do estado de saúde do cluster a partir da telemetria do APM; camadas no contexto do K8s e do ML quando disponíveis.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">apm-service-dependencies</td><td style="border: 1px solid #ccc; padding: 4px;">Elastic APM</td><td style="border: 1px solid #ccc; padding: 4px;">Gráfico de dependência de serviço — upstream/downstream, protocolos, volume de chamadas.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">k8s-blast-radius</td><td style="border: 1px solid #ccc; padding: 4px;">Métricas do Kubernetes</td><td style="border: 1px solid #ccc; padding: 4px;">Impacto da falha do Node: falha total, degradado, não afetado, viabilidade de reagendamento.</td>
                    </tr>
              </tbody>
            </table>
          <h3><strong>Cluster health rollup</strong></h3><p>Pergunte &quot;o que está com defeito?&quot; ou &quot;me dê um relatório de status&quot; e receba uma orientação instantânea: indicador geral de integridade, serviços degradados com as respectivas causas, principais consumidores de memória dos pods, detalhamento da gravidade das anomalias e taxa de transferência do serviço, tudo em uma única visualização embutida. Este é o ponto de partida quando algo parece errado, mas você não sabe onde procurar. A visualização se adapta com base nos recursos compatíveis com a sua implantação. O APM fornece informações sobre a saúde do serviço. As métricas de Kubernetes adicionam contexto ao pod e ao nó. Os trabalhos de ML adicionam anomalias.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f31be8bbcffd54623fcec4e51fd914dae666c026-1510x1810.png" style="width: 100%;" /><h3><strong>Gráfico das dependências dos serviços</strong></h3><p>Pergunte &quot;o que aciona o checkout?&quot; ou &quot;mostre-me a topologia&quot; e tenha um gráfico de dependências em camadas, autores de chamada upstream, dependências downstream, protocolos, volume de chamadas e latência por edge. Vamos pedir ao Claude para &quot;mostrar as dependências de serviço do frontend&quot;:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2afec7dbd2153bc3a9f05068f323911af766a258-2048x1184.png" style="width: 100%;" /><p>Aumente o zoom, mova a câmera e passe o cursor sobre a imagem para ter todos os detalhes necessários para compreender as complexas relações de serviço:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ff99d2fd0a54c22c9f7b975e9d8d7073c4311e7a-2048x970.png" style="width: 100%;" /><h3><strong>Avalie o risco com um raio de explosão</strong></h3><p>Pergunte &quot;o que acontece se meu nó do k8s cair?&quot; e tenha um diagrama de impacto radial: o nó alvo no centro, implantações com interrupção total em vermelho, interrupções degradadas em cor âmbar e as não afetadas em cinza. Um cartão flutuante mostra os pods em risco e a viabilidade de reagendamento. Implantações de réplica única são sinalizadas como pontos únicos de falha. </p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7736bef9609e45a3f0c7f9f3090a314b0834315d-1272x1838.png" style="width: 100%;" /><h3><strong>Observe</strong></h3><p>O primitivo de acesso primário do agente para Elastic — uma ferramenta, três modos para três necessidades diferentes. Diga &quot;o que a CPU está fazendo agora?&quot; e ele executa um ES|QL uma vez e retorna uma tabela. Diga &quot;mostre a latência frontend dos próximos 60 segundos&quot; e ele faz amostragem ao vivo da métrica, atualizando o gráfico no local. Fale &quot;diga-me quando a memória ficar abaixo de 80 MB&quot; ou &quot;observe qualquer coisa incomum pelos próximos 10 minutos&quot; e ela bloqueia até que a condição seja acionada ou a janela expire. A visualização se adapta ao modo: uma tabela de resultados para consultas one-shot, um gráfico de tendência ao vivo com estatísticas atuais/pico/base para amostragem e condições de limiar, e um cartão de gatilho pontuado por gravidade para o modo anomalia.</p><h3>Como funciona</h3><p>Mesmo padrão de MCP App que os apps do Security e Search: um servidor Node.js, seis ferramentas voltadas para modelos conectadas a seis recursos de visualização de arquivo único. As ferramentas são agrupadas por backend de implantação (Universal, dependente de APM, dependente de K8s, dependente de ML), para que o agente e o usuário saibam antecipadamente quais ferramentas se aplicam a uma determinada implantação, em vez de descobrir lacunas de capacidade no momento da chamada. O MCP App também inclui um exemplo de fluxo de trabalho do Agent Builder: <code>k8s-crashloop-investigation-otel</code> que pode ser acionado por um alerta do Kubernetes e retornar um resumo estruturado da causa raiz antes de você ter aberto um único dashboard.</p><a href="https://videos.elastic.co/watch/xcF3nKDziVgphc5bPAkv9R">Watch video on Vidyard</a><h2>A pilha agêntica, interativa</h2><p>Três propriedades desse padrão merecem ser mencionadas diretamente. Primeiro, o resultado da ferramenta não é mais o fim do trabalho, mas o início: a conversa retorna uma interface na qual você pode agir, e não um resumo do qual você precisa partir. Segundo, o mesmo agente, o mesmo contexto do modelo e a mesma linha de conversa agora podem se mover por superfícies do Security, Search e do Observability sem sair da conversa. Terceiro, isso só funciona porque o Elasticsearch e o Kibana já expõem as APIs. O MCP App é uma camada interativa fina sobre as funcionalidades do produto que já enviamos.</p><p>O Attack Discovery já alimenta a visualização de descobertas correlacionadas dentro deste app. Dentro da pilha, o mesmo padrão agêntico vai além: o Elastic Workflows automatiza as etapas determinísticas (enriquecer entidades, criar casos, isolar hosts), enquanto o Agent Builder raciocina sobre os dados e invoca esses fluxos de trabalho como ferramentas. O MCP App traz essa mesma superfície de segurança para a conversa externa; o Workflows e o Agent Builder a aprofundam na pilha. Pontos de entrada diferentes, mesmas APIs da Elastic por trás de tudo.</p><p><strong>Experimente:</strong></p><ul><li><strong>Security:</strong> <a href="https://github.com/elastic/example-mcp-app-security">example-mcp-app-security</a></li><li><strong>Search e dashboards:</strong> <a href="https://github.com/elastic/example-mcp-dashbuilder">example-mcp-dashbuilder</a></li><li><strong>Observability: </strong><a href="https://github.com/elastic/example-mcp-app-observability">example-mcp-observability</a></li></ul><p>Ainda não tem um cluster Elasticsearch? Inicie uma <a href="https://cloud.elastic.co/registration">avaliação gratuita do Elastic Cloud</a>. Para mais informações sobre os componentes básicos do aplicativo de segurança, consulte as postagens relacionadas do Security Labs sobre <a href="https://www.elastic.co/security-labs/from-alert-fatigue-to-agentic-response">Elastic Workflows e Agent Builder</a>, <a href="https://www.elastic.co/security-labs/agent-skills-elastic-security">Habilidades de agentes</a> e <a href="https://www.elastic.co/security-labs/speeding-apt-attack-discovery-confirmation-with-attack-discovery-workflows-and-agent-builder">Descoberta de ataques</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apresentando chaves de API unificadas para Elastic Cloud Serverless e Elasticsearch]]></title>
            <description><![CDATA[Saiba como a Elastic unificou o plano de controle e a autenticação do plano de dados no Serverless com uma arquitetura IAM distribuída globalmente. Use uma chave de API para as APIs da nuvem e do Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elastic-cloud-api-keys-unified-serverless</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elastic-cloud-api-keys-unified-serverless</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[ Alex Chalkias]]></dc:creator>
            <pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/864e229f00eb2018084f13dd7f0e390e18383ed4-1980x1188.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Imagine você um engenheiro de confiabilidade de sistemas (SRE) responsável por uma frota crescente de projetos do Elastic Cloud Serverless: Elastic Observability para sua infraestrutura de produção, Elastic Security para sua equipe do centro de operações de segurança (SOC) e Elasticsearch para sua aplicação voltada ao cliente. Cada projeto tem a própria chave de API do Elasticsearch. Seu pipeline de integração contínua e entrega contínua (CI/CD) precisa de uma chave separada da Cloud API para provisionar e gerenciar esses projetos. O dia de rotação chega todo trimestre: você passa por cada projeto, gera novas chaves, atualiza o state do Terraform, reimplanta os pipelines e torce para que nada fique para trás. Quando um incidente acontece às 2h e é preciso revogar o acesso rápido, você se vê comparando uma planilha de credenciais para saber qual chave pertence a qual projeto e a qual serviço.</p><p>Hoje, essa história fica muito mais simples. <strong>Chaves de API do Elastic Cloud</strong> agora podem ser usadas para autenticar diretamente em comparação a APIs <strong>do Elasticsearch</strong> e <strong>Kibana</strong> no <strong>Elastic Cloud Serverless</strong>. Agora você pode usar uma única credencial para gerenciar os recursos da sua <em>organização e</em> executar operações de dados, como a Elasticsearch Query Language (ES|QL), ingestão de dados e alertas.</p><p>Vamos ver por que construímos isso, como projetamos uma camada de identidade distribuída globalmente para possibilitar o recurso e como ele estabelece a base para a busca entre projetos.</p><h2>O ônus da gestão de segredos</h2><p>Construir pipelines confiáveis de CI/CD, fluxos de trabalho GitOps ou automação Terraform em plataformas de dados tem um custo oculto: a proliferação de segredos.</p><p>No modelo anterior, os desenvolvedores lidavam com uma história de autenticação desarticulada:</p><ul><li><strong>Plano de controle (chaves da API do Elastic Cloud):</strong> chaves com escopo organizacional usadas para criar projetos, convidar usuários e gerenciar as cobranças via <a href="https://www.elastic.co/docs/api/doc/cloud/">API do Elastic Cloud</a>.</li><li><strong>Plano de dados (chaves da API do Elasticsearch):</strong> Chaves com escopo de projeto criadas <em>dentro</em> de um projeto Serverless específico para interagir com as APIs do <a href="https://www.elastic.co/docs/api/doc/elasticsearch-serverless/">Elasticsearch</a> e do <a href="https://www.elastic.co/docs/api/doc/serverless">Kibana</a>.</li></ul><p>Nesse caso, seu script de implantação precisava se autenticar no Elastic Cloud, provisionar um projeto Serverless, extrair uma chave de API do Elasticsearch recém-criada desse projeto específico e, em seguida, inserir <em>essa</em> segunda chave na aplicação ou na ferramenta de automação mais adiante, o que resultava em pipelines complexos, logs de auditoria fragmentados e maior risco de vazamento de credenciais.</p><h2>Autenticação unificada no Elastic Cloud Serverless</h2><p>Com este lançamento, a separação para projetos Serverless foi eliminada. Agora você pode criar uma chave de API do Elastic Cloud explicitamente autorizada para <strong>nuvem, Elasticsearch e Kibana APIs</strong>.</p><ul><li><strong>Antes:</strong> a chave de API do Elastic Cloud era estritamente um token do plano de controle. Ela podia criar projetos, gerenciar cobranças e convidar usuários, mas tinha um limite rígido; não podia ser usada para chamar as APIs do Elasticsearch nem Kibana dentro desses projetos. Você sempre precisava de uma segunda chave específica do projeto para operações de dados.</li><li><strong>Agora:</strong> ao ter acesso a <strong>nuvem, Elasticsearch e a API Kibana</strong> ao criar uma chave de API do Elastic Cloud, o limite rígido é retirado para o Serverless. Essa chave de API se torna uma credencial verdadeiramente unificada. Ela mantém a capacidade de gerenciar a infraestrutura da sua organização, ao mesmo tempo que ganha acesso nativo para consultar, ingerir e analisar dados em qualquer projeto Serverless autorizado.</li></ul><p>Ao unificar tudo sob uma única chave de API do Elastic Cloud, você ganha uma única identidade que pode ter escopo definido, ser auditada, rotacionada e revogada como uma unidade. Cada chamada de API, seja para provisionar um novo projeto ou executar uma consulta ES|QL, aparece sob a mesma credencial nos seus logs de auditoria, fornecendo um único rastro a ser seguido durante investigações de incidentes ou revisões de conformidade. A rotação de credenciais agora é feita em uma etapa em vez de ser uma atualização coordenada em segredos separados do plano de controle e do plano de dados. E, como as alocações de função são por projeto, uma só chave pode abranger vários projetos, gerenciando a ingestão no seu projeto de observabilidade e executando consultas no seu projeto de segurança, sem precisar lidar com credenciais separadas para cada um.</p><p>Importante: <em>unificado</em> não significa <em>todo-poderoso</em>. Ao usar a carga útil <code>role_assignments</code>, você pode definir uma chave unificada estritamente para um único projeto e uma função específica (como somente leitura), garantindo que o raio de explosão continue totalmente contido caso uma credencial seja exposta. Se um desenvolvedor sair ou uma aplicação for desativada, você pode revogar uma única chave do console Elastic Cloud, encerrando imediatamente o acesso tanto no plano de controle quanto em todos os projetos Elasticsearch associados.</p><p><em>(Atenção: nas implantações Elastic Cloud Hosted/gerenciadas, as chaves da API da nuvem ainda gerenciam apenas o plano de controle. O suporte para estender isso às APIs de pilha hospedada está planejado para uma versão futura.)</em></p><h2>Automatizando seus fluxos de trabalho</h2><p>Começar é simples. Você pode configurar inteiramente no console Elastic Cloud ou automatizar usando a <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">API do Elastic Cloud</a>.</p><p>O processo da IU não muda, mas agora você pode selecionar <strong>Nuvem, Elasticsearch e API Kibana</strong> na alocação de função do projeto.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c4f802f130655290cd474b283001a954d14c3088-2801x1681.png" style="width: 100%;" /><p>Veja como criar uma chave unificada programaticamente usando a API do Elastic Cloud. Observe o <code>application_roles</code> conjunto, pois é o que concede ao principal acesso nativo ao plano de dados do Elasticsearch:</p><pre><code>curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: ApiKey $EC_API_KEY" \
  "https://api.elastic-cloud.com/api/v1/users/auth/keys" \
  -d '{
    "description": "unified-automation-key",
    "expiration": "90d",
    "role_assignments": {
      "project": {
        "elasticsearch": [
          {
            "role_id": "elasticsearch-admin",
            "organization_id": "YOUR_ORG_ID",
            "all": false,
            "project_ids": ["YOUR_PROJECT_ID"],
            "application_roles": ["admin"]
          }
        ]
      }
    }
  }'</code></pre><p>Uma vez criado, você passa exatamente essa mesma chave no cabeçalho <code>Authorization: ApiKey</code> tanto para <code>api.elastic-cloud.com</code> quanto para seus endpoints específicos do Serverless Elasticsearch.</p><h2>Nos bastidores: construindo uma camada de identidade distribuída</h2><p>Fazer uma chave da API da nuvem funcionar tanto no plano de controle quanto no plano de dados não é tão simples como passar um token. É preciso resolver um desafio fundamental nos sistemas distribuídos.</p><p>Historicamente, as chaves de API da nuvem ficavam em um cluster de segurança global centralizado. Isso funciona nas operações de plano de controle cuja latência mais alta é aceitável. No entanto, requisições de dados do Elasticsearch exigem latência ultrabaixa. Não podemos viajar pelo globo até um plano de controle central para validar cada busca ou solicitação de ingestão.</p><p>Para resolver, introduzimos uma nova arquitetura de autenticação apoiada por um datastore distribuído globalmente. O diagrama sequencial a seguir mostra um cliente enviando uma consulta Elasticsearch, usando uma chave API do Elastic Cloud, ilustrando como a autenticação ocorre inteiramente dentro da região, sem a viagem por todo o plano de controle global. O Elasticsearch delega a autenticação ao Serviço IAM Regional, que valida a chave e resolve as alocações de função em uma réplica local do banco de dados distribuído globalmente. Uma vez autorizado, o Elasticsearch executa a consulta e retorna os resultados ao cliente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3e38d7a862b9981523c5393c441b92eae13aeb90-2401x1351.webp" style="width: 100%;" /><h3>Persistência distribuída globalmente</h3><p>Em vez de depender exclusivamente de um cluster de segurança centralizado, as chaves de API do Elastic Cloud e as respectivas definições de função agora ficam em um banco de dados globalmente distribuído e de alta disponibilidade. Esse banco de dados sincroniza os dados de gerenciamento de identidade e acesso (IAM) no plano de controle global e nos planos de dados regionais onde seus projetos Serverless são executados.</p><h3>Validação local com IAM regional</h3><p>Quando seu cliente envia uma requisição para o Elasticsearch usando uma chave API do Elastic Cloud, a solicitação não retorna ao plano de controle global. Em vez disso, ela é encaminhada para o novo serviço regional IAM. Ele valida a chave em relação à réplica do banco de dados local, garantindo que a autenticação ocorra com latência quase zero e completamente isolada de interrupções no plano de controle global.</p><h3>Mapeamento dinâmico de funções</h3><p>A autenticação é metade do caminho; o sistema também precisa autorizar a solicitação. O serviço IAM regional traduz na hora suas alocações de função no nível da nuvem, por exemplo, <code>application_roles</code>), em privilégios nativos do Elasticsearch. O Elasticsearch pode então autorizar e executar a solicitação no local, sem precisar de um <code>.security</code> índice local.</p><h2>A base para a busca entre projetos</h2><p>Essa arquitetura de identidade distribuída é um elemento fundamental para o futuro da plataforma Elastic.</p><p>Como a identidade e o acesso agora estão unificados e sincronizados globalmente, temos o framework necessário para transmitir sua identidade com segurança entre diferentes projetos. Isso possibilita as futuras capacidades <strong>de Busca Cruzada por Projetos (CPS)</strong> para Serverless.</p><p>Com o CPS, você poderá consultar dados que abrangem vários projetos Serverless remotos, como combinar cargas de trabalho de segurança e observabilidade, como se fossem um único conjunto de dados. Ao depender de chaves de API unificadas, o sistema pode avaliar automaticamente suas permissões simultaneamente em todos os projetos, sem exigir que você configure relacionamentos de confiança complexos, certificados ou credenciais duplicadas em cada projeto-alvo.</p><h2>Saiba mais</h2><p>Pronto para simplificar sua pilha?</p><ul><li>Leia a <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">documentação das chaves de API do Elastic Cloud</a> para aprender como atribuir acesso ao stack.</li><li>Confira a referência <a href="https://www.elastic.co/docs/api/doc/cloud/operation/operation-create-api-key">Criar chave de API (Elastic Cloud API)</a> para automatizar a geração de chaves.</li><li>Consulte <a href="https://www.elastic.co/docs/deploy-manage/api-keys">Chaves de API Elastic</a> para uma comparação completa dos tipos de chave em toda a plataforma Elastic.</li></ul><p>Comece ou continue construindo no <a href="https://cloud.elastic.co/registration">Elastic Cloud</a> hoje.</p><h2>Aviso de isenção</h2><p>O lançamento e o tempo de amadurecimento de todos os recursos ou funcionalidades descritos neste artigo permanecem a exclusivo critério da Elastic. Os recursos ou funcionalidades não disponíveis no momento poderão não ser entregues ou não chegarem no prazo previsto.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/unsupervised-document-clustering-elasticsearch-jina-embeddings</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Matthew Adams]]></dc:creator>
            <pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/662c00694c3e0c2fb2128098bdb6813df9e86a72-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" style="width: 100%;" /><p><strong>O que você vai descobrir:</strong></p><ul><li>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?</li><li>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>.</li><li>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?</li><li>Como as cadeias temporais de histórias conectam clusters diários para mostrar como os temas evoluem dia após dia.</li></ul><div style="display:none">Unknown block type "infoPanel", specify a component for it in the `components.types` option</div><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><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></li><li><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.</li><li><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.</li><li><strong>Clustering global + vinculação temporal diária:</strong> descoberta e evolução da narrativa.</li></ul><p><strong>O que você precisará:</strong></p><ul><li>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.</li><li>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.</li><li>Uma <a href="https://bonobo.capi.gutools.co.uk/register/developer">chave de API do Guardian</a> (gratuita).</li></ul><h2>Configuração</h2><p>Instale os pacotes necessários:</p><pre><code>pip install elasticsearch pandas numpy plotly umap-learn python-dotenv pydantic-settings datasets requests</code></pre><p>Opcional (somente se você executar ferramentas de scraping deste repositório):</p><pre><code>pip install beautifulsoup4</code></pre><p>Depois, configure chaves de API em um arquivo <code>.env</code> na raiz do projeto:</p><pre><code>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</code></pre><p>Este notebook chama <code>load_dotenv(override=True)</code>, portanto os valores locais <code>.env</code> têm precedência.</p><pre><code>Connected to Elasticsearch</code></pre><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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Tarefa</td><td style="border: 1px solid #ccc; padding: 4px;">Preparado para</td><td style="border: 1px solid #ccc; padding: 4px;">Caso de uso</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">retrieval.passage</td><td style="border: 1px solid #ccc; padding: 4px;">Correspondência entre consulta e documento</td><td style="border: 1px solid #ccc; padding: 4px;">Busca, retrieval augmented generation (RAG)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">clustering</td><td style="border: 1px solid #ccc; padding: 4px;">Agrupamento de tópicos (otimizado para clusters compactos)</td><td style="border: 1px solid #ccc; padding: 4px;">Descoberta, categorização</td>
                    </tr>
              </tbody>
            </table>
          <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><pre><code>    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</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9bcf7a744900560c1c6c63a2dc3af2f9bfd33e11-1100x500.png" style="width: 100%;" /><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=&quot;clustering&quot;</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><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.</li><li><strong>The Guardian</strong> através da <a href="https://open-platform.theguardian.com/">API da Guardian Open Platform</a>.</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><pre><code>    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...</code></pre><h3>Embedding com a tarefa de clustering</h3><p>A API Jina v5 é chamada com <code>task=&quot;clustering&quot;</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><pre><code>payload = {
    "model": "jina-embeddings-v5-text-small",
    "input": texts,
    "task": "clustering",  # ← This selects the clustering LoRA adapter
}</code></pre><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><pre><code>    Embeddings ready: 8,495 vectors of dimension 1024
    Time: 0.6s</code></pre><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><pre><code>{
  "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
    }
  }
}</code></pre><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><pre><code>    Indexed 8,495 documents into docs-clustering-all
    Time: 57.5s</code></pre><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><strong>Amostra de 5% de documentos</strong> como sondas de densidade (amostra aleatória, mínimo de 50).</li><li><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.</li><li><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).</li><li><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.</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><pre><code>    Clustered global index in 31.6s
      Total clusters: 82
      Total noise:    2420 (28.5%)
      Density probes: 425 kNN queries via 9 _msearch HTTP calls</code></pre><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><pre><code>{
  "size": 0,
  "query": { "term": { "cluster_id": "72" } },
  "aggs": {
    "label_terms": {
      "significant_text": {
        "field": "text",
        "size": 5,
        "filter_duplicate_text": true
      }
    }
  }
}</code></pre><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><pre><code>    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%)</code></pre><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99099f5adaa945ae4097c50b0d7151c7dd28872e-1000x400.png" style="width: 100%;" /><p>Distribuição diária de documentos agrupados versus ruídos ao longo de fevereiro de 2025.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/477c6c7177942955a942f85f5c881da50e517915-1100x700.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4f4302556c8974c416842452cf33bca06e90b966-1100x700.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/327f96a79e382ef30614cb0570aa7fccd822b8f8-1100x700.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/acfb1524a10e24d6ff2412e7c3ec0f2b3ac75193-900x600.png" style="width: 100%;" /><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>λ = 1,0 → relevância pura (o mesmo que kNN simples).</li><li>λ = 0,0 → diversidade pura (resultados de distribuição máxima).</li><li>λ = 0,5 → equilibrado: relevante para o tópico, mas abordando diferentes perspectivas.</li></ul><div style="display:none">Unknown block type "infoPanel", specify a component for it in the `components.types` option</div><p>Uma forma mínima de solicitação de recuperador é assim:</p><pre><code>{
  "size": 8,
  "retriever": {
    "diversify": {
      "type": "mmr",
      "field": "embedding",
      "lambda": 0.5,
      "query_vector": "<cluster-centroid-vector>",
      "retriever": {
        "knn": {
          "field": "embedding",
          "query_vector": "<cluster-centroid-vector>",
          "k": 50,
          "num_candidates": 100
        }
      }
    }
  }
}</code></pre><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: &quot;O que esse cluster cobre de fato?&quot; em vez de apenas &quot;Qual é o ponto central?&quot;</p><pre><code>    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</code></pre><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><strong>Entender o que um cluster cobre</strong>, não apenas o centro, mas também as bordas.</li><li><strong>Geração de resumos</strong>. Documentos representativos diversos oferecem um material melhor para um LLM.</li><li><strong>Encontrar exemplos representativos</strong> para análise humana ou rotulagem posterior.</li><li><strong>Verificações de qualidade</strong>. Se os resultados diversos parecerem incoerentes, o cluster pode precisar ser dividido.</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>Você pode ver uma amostra de alguns documentos representativos.</li><li>Executar kNN contra o índice do dia B.</li><li>Conte quantos acessos caem em cada cluster B do dia.</li><li>Se a fração de acerto ultrapassar um limite (fração de kNN ≥ 0,4), registre um link.</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><pre><code>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' -> 2025.02.02 'league | season | striker'  (100%)
  2025.02.03 'league | striker | loan' -> 2025.02.04 'league | striker | season'  (100%)
  2025.02.03 'score | operator | gedling' -> 2025.02.04 'league | striker | season'  (100%)
  2025.02.12 'playoff | leg | bayern' -> 2025.02.13 'league | players | injury'  (100%)
  2025.02.14 'league | injury | football' -> 2025.02.15 'league | premier | football'  (100%)
  2025.02.18 'russia | ukraine | talks' -> 2025.02.19 'saudi | russia | arabia'  (100%)
  2025.02.18 'football | league | bayern' -> 2025.02.19 'league | manchester | players'  (100%)
  2025.02.21 'league | premier | manchester' -> 2025.02.22 'game | players | defeat'  (100%)
  2025.02.21 'rugby | calcutta | brilliant' -> 2025.02.22 'game | players | defeat'  (100%)
  2025.02.26 'metals | kyiv | ukrainian' -> 2025.02.27 'ukraine | russia | talks'  (100%)</code></pre><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 &quot;política do Reino Unido&quot; 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.<br/></p><pre><code>    Strong links (kNN fraction >= 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)</code></pre><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/100a60a7fb85da8ab3813fd071a82c93f2c3f318-1300x650.png" style="width: 100%;" /><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><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.</li><li><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.</li><li><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).</li><li><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.</li><li><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.</li></ol><p><strong>Principais conclusões:</strong></p><ul><li>O tipo de tarefa de incorporação importa: embeddings de clustering produzem grupos tópicos mensuravelmente mais compactos.</li><li>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>.</li><li>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.</li><li><code>significant_text</code> é rápido, compreensível e eficaz tanto para autorrotulagem quanto para controle de qualidade.</li></ul><p><strong>Quando essa abordagem é útil:</strong></p><ul><li>Você tem texto com carimbo de data e hora e quer descobrir tópicos sem dados de treinamento rotulados.</li><li>Você precisa de uma plataforma para armazenamento, busca vetorial, rotulagem e ligação temporal.</li></ul><p><strong>Extensões para explorar:</strong></p><ul><li>Clustering por múltiplos períodos (semanal, pacotes mensais).</li><li>Ingestão em tempo real com atribuição incremental de cluster.</li><li>Resumos de cluster gerados pelo LLM usando os termos significant_text como sementes.</li><li>Em escala maior, centroides KMeans mostrados podem servir como sementes de aquecimento para clustering baseado em densidade, reduzindo o custo da fase da sonda.</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><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.</li><li><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.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Por que a busca para e-commerce precisa de governança]]></title>
            <description><![CDATA[Descubra por que a busca para e-commerce falha sem governança e como uma camada de controle garante resultados previsíveis e orientados pela intenção, melhorando a recuperação.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ecommerce-search-governance-improve-retrieval</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ecommerce-search-governance-improve-retrieval</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Marquardt, Honza Král, Taylor Roy]]></dc:creator>
            <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3793259b01a5653a7520393a2f006610de0d21e7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Os varejistas de e-commerce precisam lidar com vários tipos de consultas fundamentalmente diferentes dentro do mesmo sistema. Um comprador que procura por “laranjas” espera a fruta, não produtos que contenham a palavra “laranja”, como suco de laranja ou geleia de laranja, e não produtos cítricos semanticamente relacionados. Um comprador que procura um &quot;presente para o avô que gosta de doces&quot; precisa de uma descoberta semântica, não de uma correspondência literal de palavras-chave.</p><p><em>Recuperação lexical</em> (correspondência de texto), <em>recuperação semântica</em> (correspondência de conceitos) e <em>recuperação híbrida</em> (combinação de sinais lexicais e semânticos) não resolvem esses problemas por si só. A recuperação lexical pode retornar qualquer conteúdo que contenha a palavra &quot;laranjas&quot;, enquanto a recuperação semântica pura, em uma consulta com alta intenção como &quot;laranjas&quot;, pode ampliar o escopo para itens relacionados, como limões ou toranjas. A recuperação híbrida mescla esses sinais lexicais e semânticos, mas ainda não determina se essa consulta deve ser tratada como uma consulta de navegação, quais restrições devem ser impostas ou quais políticas de negócios se aplicam. A lacuna não está na tecnologia de recuperação em si; está na ausência de uma camada de governança que entenda que tipo de consulta é esta e quais restrições devem ser impostas antes de a recuperação começar.</p><p>Neste blog, exploramos a governança de busca para e-commerce, por que isso é importante e como uma camada de controle garante uma recuperação previsível e precisa.</p><h2>O que significa governança na busca para e-commerce</h2><p><em>Governança</em>, neste contexto, significa introduzir uma camada de decisão entre a consulta do usuário e o mecanismo de recuperação de dados. Esta camada realiza as seguintes funções:</p><ul><li>Classifica a intenção da consulta: isso é navegação (&quot;laranjas&quot;) ou descoberta (&quot;presente para o avô&quot;)?</li><li>Aplica restrições comerciais: Quais limites de categoria, regras de elegibilidade, restrições de disponibilidade ou políticas de comercialização se aplicam?</li><li>Caminhos para a estratégia apropriada: deve-se usar recuperação lexical, semântica ou híbrida?</li></ul><p>Uma camada de governança determina qual abordagem de recuperação deve ser usada para cada consulta, quais restrições devem ser aplicadas e quais políticas de negócios devem ser aplicadas antes do início da recuperação. É importante não confundir governança com recuperação híbrida: híbrida é uma estratégia de recuperação que combina sinais lexicais e semânticos, enquanto a governança é a camada inicial de decisão que determina se deve ser usada a recuperação lexical, semântica ou híbrida.</p><h2>O status quo: a implementação da camada de aplicação &quot;spaghetti&quot;</h2><p>Atualmente, muitos varejistas tentam resolver isso inserindo lógica diretamente na camada de aplicação. Isso geralmente resulta em <em>código espaguete</em>, ou seja, milhares de linhas de instruções if-then fixas no código, regex e templates de busca complexos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f532b099ee103458e15563a711dae92952f8df02-1024x765.png" style="width: 100%;" /><p>Essa abordagem pode fornecer resultados de busca desejados, como mostrado acima; no entanto, ela cria atritos operacionais significativos:</p><ul><li><strong>Dependência da engenharia:</strong> usuários da área de negócios e a equipe de merchandising não conseguem modificar o comportamento de busca sem abrir chamados para a engenharia e enfrentar longos ciclos de implantação, que muitas vezes levam várias semanas.</li><li><strong>Fragmentação:</strong> a lógica de busca fica dispersa entre o código da aplicação e os modelos de busca, sendo difícil de explicar ou auditar, tornando arriscado evoluir.</li></ul><p>Mesmo quando as equipes reconhecem a necessidade de roteamento, o debate frequentemente se concentra na questão errada: qual método de recuperação escolher.</p><h2>A falsa escolha: lexical vs. semântico vs. híbrido</h2><p>As equipes de busca costumam enquadrar o desafio como uma escolha de estratégia de recuperação: lexical/BM25 versus semântica/vetores versus híbrida. Esse enquadramento é compreensível (os métodos de recuperação são importantes), mas ignora a falha mais comum em implantações reais: usar uma única abordagem de recuperação para todas as consultas gera resultados abaixo do ideal.</p><p>A busca de comércio é uma combinação de intenções fundamentalmente diferentes:</p><ul><li><strong>Navegação determinística e de alta intenção</strong> (&quot;laranjas&quot;, &quot;leite&quot;, &quot;chocolate sem amendoim&quot;, &quot;azeite de oliva barato&quot;).</li><li><strong>Descoberta exploratória</strong> (&quot;jaqueta para caminhar nas montanhas&quot;, &quot;presente para uma criança de 12 anos que gosta de robótica&quot;).</li><li><strong>Restrições operacionais</strong> (disponibilidade, tamanho, preço, cor).</li><li><strong>Merchandising e campanhas</strong> (impulsionar, enterrar, campanhas sazonais).</li></ul><p>Quando o sistema encaminha todos esses elementos pela mesma estratégia de recuperação, os resultados frequentemente apresentam erros sistemáticos e previsíveis, devido à falta de governança no modelo operacional. Quando as equipes não percebem isso como uma lacuna de governança, elas recorrem à única ferramenta que possuem: mais ajustes.</p><h2>Por que o &quot;ajuste de relevância&quot; pode se tornar cíclico</h2><p>Sem uma camada de roteamento, a &quot;relevância&quot; frequentemente se transforma em um amontoado interminável:</p><ul><li>Por que essa consulta mostra acessórios acima do produto núcleo?</li><li>Por que essa consulta principal passou a exibir itens relacionados de repente?</li><li>Por que os resultados mudaram depois que adicionamos sinônimos, ajustamos analisadores ou ativamos a funcionalidade híbrida?</li><li>Por que a equipe de negócios precisa de um release de engenharia para corrigir uma única consulta?</li></ul><p>As equipes respondem com mais ajustes: mais sinônimos, mais impulsos, mais experimentos de reclassificação, mais exceções no código da aplicação. Isso pode funcionar por um tempo, mas frequentemente produz um comportamento frágil, porque o sistema ainda não possui uma camada de decisão explícita para determinar o tipo de consulta e impor as restrições corretas antes da recuperação.</p><h2>A anatomia da intenção do e-commerce: cabeça e cauda</h2><p>Nesta seção, usamos &quot;cabeça&quot; e &quot;cauda&quot; como abreviações práticas para padrões comuns de navegação e exploração de consultas no comércio eletrônico. No mundo real, muitas consultas contêm aspectos de ambos:</p><h3>Consultas principais (intenção determinística)</h3><p>São consultas diretas e navegacionais onde o usuário sabe exatamente o que quer:</p><ul><li>Intenção de item único (&quot;laranjas&quot;, &quot;leite&quot;, &quot;pão&quot;).</li><li>Marcas exatas ou famílias de produtos (&quot;iPhone 15 Pro&quot;, &quot;Coca Coke&quot;).</li><li>SKUs, números de modelo, tamanhos (&quot;ABC123&quot;, &quot;Air Max 270&quot;).</li></ul><p>Para essas consultas, a recuperação lexical pode lidar com correspondência de tokens (palavras correspondentes), mas o negócio também espera respeitar restrições, devolver rankings previsíveis e ter resultados controláveis. Um profissional de merchandising precisa garantir que uma consulta seja resolvida dentro dos limites da categoria correta, respeite os critérios de elegibilidade e destaque as prioridades específicas do negócio.</p><p>A governança é necessária para fazer cumprir a resolução pretendida. Por exemplo, &quot;laranjas&quot; devem corresponder à categoria de hortifrúti, não a suco de laranja, geleia de laranja ou refrigerante de laranja.</p><h3>Consultas de cauda (descoberta exploratória)</h3><p>São consultas descritivas e ricas em intenção, nas quais os consumidores exploram:</p><ul><li>&quot;Presente para o avô que adora doces&quot;</li><li>&quot;Jaqueta para caminhadas nas montanhas&quot;</li><li>&quot;Sapatos para ficar em pé o dia todo&quot;</li></ul><p>A recuperação lexical costuma apresentar dificuldades nesse caso. A recuperação semântica se destaca porque pode conectar o conceito de consulta ao produto, mesmo quando a redação não corresponde. Mas a recuperação semântica sozinha raramente é suficiente também. Consultas reais frequentemente exigem restrições para serem aplicadas, independentemente do método de recuperação usado.</p><h2>As restrições são ortogonais ao método de recuperação</h2><p>Aplicar restrições à recuperação semântica não significa <em>busca híbrida</em>. São conceitos ortogonais. Restrições, como filtros e boosts no Elasticsearch, podem ser aplicadas a qualquer recuperação lexical, semântica ou híbrida. O desafio é decidir como a consulta deve ser interpretada, quais restrições devem ser aplicadas e qual estratégia de recuperação deve ser utilizada.</p><p>Abaixo estão alguns exemplos de consultas que combinam recuperação com restrições rígidas:</p><ul><li><strong>Laranjas:</strong> recuperação lexical para &quot;laranjas&quot; mais uma restrição de categoria, como &quot;Frutas&quot; ou &quot;Produtos&quot;, eliminando geleia de laranja, suco de laranja e refrigerante de laranja.</li><li><strong>Frutas com alto teor de vitamina C por menos de US$ 4:</strong> recuperação semântica com foco em intenção nutricional, além de restrições que limitam os resultados à categoria de frutas e produtos por menos de US$ 4.</li><li><strong>Sapatos confortáveis para trabalhar:</strong> recuperação semântica para intenção contextual mais uma restrição de categoria que limita os resultados a sapatos.</li></ul><p>Essas consultas não podem ser tratadas por uma única abordagem:</p><ul><li><strong>A recuperação lexical pura</strong> geralmente é insuficiente aqui porque frases como &quot;rico em vitamina C&quot; ou &quot;confortável&quot; podem não existir como atributos bem definidos e estruturados. Talvez seja necessário inferi-las a partir de descrições, análises ou especificações do produto.</li><li><strong>A recuperação semântica pura</strong> também nem sempre é suficiente, pois, sem restrições explícitas, uma consulta como &quot;frutas ricas em vitamina C&quot; pode se expandir para suplementos vitamínicos, bebidas com sabor de frutas ou vegetais ricos em vitaminas fora da categoria e faixa de preço pretendidas.</li></ul><p>Uma camada de governança determina se uma consulta precisa de recuperação lexical, compreensão semântica, aplicação de restrições ou alguma combinação dessas. Sem essa camada, as equipes de comércio eletrônico podem acabar:</p><ul><li><strong>Excesso de restrições:</strong> usar a recuperação lexical para pedidos semânticos (por exemplo, &quot;presente para o avô&quot;).</li><li><strong>Sub-restrição: </strong>utilizar consultas semânticas para consultas principais de alta intenção (por exemplo, &quot;laranjas&quot;).</li></ul><p>O desafio da governança é construir um sistema que possa tomar a decisão correta para cada classe de consulta.</p><h2>O que acontece sem governança</h2><p>O modo de falha mais comum é simples: as equipes pegam a consulta bruta do usuário e a encaminham diretamente para uma única estratégia de recuperação (lexical, semântica ou híbrida), sem uma camada intermediária de governança.</p><h3>A recuperação lexical falha na resolução pretendida</h3><p>Quando um usuário pesquisa por “laranjas”, uma estratégia de recuperação lexical pode retornar qualquer resultado que contenha esse token: suco de laranja, geleia de laranja ou refrigerante de laranja. O sistema encontrou o termo corretamente, mas, sem governança, pode não resolver o contexto de compra pretendido (a fruta).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99abc7a46f9c56a26a68d0a089d7ab830b9b5568-1560x814.png" style="width: 100%;" /><h3>A recuperação semântica se expande além das restrições pretendidas</h3><p>Quando um usuário busca por &quot;laranjas&quot;, um sistema semântico pode recuperar itens conceitualmente relacionados entre conceitos de produtos próximos. O sistema pode entender corretamente o domínio mais amplo (frutas ou produtos), mas, sem uma governança explícita, ele ainda pode se expandir além da restrição pretendida pelo usuário (especificamente laranjas).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c9de86363ecbed499fe48259f47b3c5b2c26bc43-1568x796.png" style="width: 100%;" /><h3>A lacuna é a governança</h3><p>O que é necessário é uma camada de decisão a montante que determine a intenção da consulta e impeça as restrições corretas antes do início da recuperação. Isso resolve questões como:</p><ul><li>Itens semelhantes ou relacionados aparecendo ao lado do que o usuário realmente queria.</li><li>Limites de categorias desfocados (&quot;bebidas&quot; versus. &quot;produtos&quot;).</li><li>Incapacidade de implementar aumentos sazonais ou campanhas.</li><li>Resultados imprevisíveis e inexplicáveis.</li></ul><h2>Compreensão e roteamento de intenções: o plano de controle necessário</h2><p>Um sistema de busca governado introduz um plano de controle leve antes da recuperação (antes de executar uma consulta no Elasticsearch). O controle será discutido em detalhes nas partes 3 e 4 desta série de blog; por enquanto, discutiremos apenas o que ele pode fazer, mas não como funciona:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/88c3d0f9731a128d73a765dcdffed897308110a6-2680x766.png" style="width: 100%;" /><p>Um plano de controle pode detectar intenção, aplicar políticas de negócio e garantir a estratégia de recuperação apropriada da seguinte forma:</p><p><strong>1. Detectar sinais de intenção</strong></p><ul><li>Essa consulta é provavelmente navegação versus descoberta?</li><li>É uma consulta conhecida como principal (leite, pão, bananas)?</li><li>Existe uma interpretação conhecida de produto, marca ou categoria (por exemplo, &quot;laranjas&quot; deve ser interpretado como hortifrúti).</li><li>A consulta segue um padrão semelhante ao SKU?</li><li>A consulta se enquadra em uma campanha ativa ou em uma política sazonal (por exemplo, durante o Natal, aumentar os resultados relacionados a peru)?</li><li>A consulta implica alguma restrição (categoria, atributos, exclusões, preço/tamanho/cor)?</li></ul><p><strong>2. Aplicar governança e políticas de negócios</strong></p><ul><li>Aplique primeiro as restrições determinísticas (categoria/atributo/negação/disponibilidade).</li><li>Aplique políticas de comercialização ativas (impulsionar/enterrar/fixar/substituir).</li><li>Resolva conflitos com regras de precedência (por exemplo, substituições de campanhas versus políticas globais).</li></ul><p><strong>3. Encaminhar para a estratégia de recuperação apropriada</strong></p><ul><li>Lexical (rápido, determinístico) para consultas de navegação/de alta intenção.</li><li>Recuperação semântica para consultas verdadeiras de descoberta.</li><li>Híbrido onde sinais lexicais e semânticos combinados agregam valor sob restrições explícitas de negócios.</li></ul><p>Na prática, a saída do plano de controle não é simplesmente “usar híbrido” ou “usar semântico”. Trata-se de um plano de recuperação de compras controlado: uma interpretação da intenção do comprador, das restrições e políticas que devem ser aplicadas e da estratégia de recuperação que deve ser executada. Alguns exemplos simples tornam isso concreto:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consulta do cliente</td><td style="border: 1px solid #ccc; padding: 4px;">Interpretação governada</td><td style="border: 1px solid #ccc; padding: 4px;">Exemplo de plano de recuperação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"Chocolate sem amendoim"</td><td style="border: 1px solid #ccc; padding: 4px;">Consulta orientada a produto com uma restrição de exclusão rígida</td><td style="border: 1px solid #ccc; padding: 4px;">Recuperação lexical para chocolate com um filtro de exclusão para produtos que contenham amendoim.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"azeite de oliva barato"</td><td style="border: 1px solid #ccc; padding: 4px;">Consulta de produto/categoria com restrição de preço</td><td style="border: 1px solid #ccc; padding: 4px;">Recuperação lexical para azeite de oliva mais um filtro de preço limitado no limite do varejista para barato</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"frutas ricas em vitamina C abaixo de $ 4"</td><td style="border: 1px solid #ccc; padding: 4px;">Consulta de descoberta que exige compreensão semântica mais restrições rígidas</td><td style="border: 1px solid #ccc; padding: 4px;">Recuperação semântica por intenção nutricional, restrita à categoria de frutas e filtrada para produtos com preço inferior a $ 4</td>
                    </tr>
              </tbody>
            </table>
          <p>Um plano de controle seleciona a política e a estratégia de recuperação adequadas para cada consulta de forma consistente, previsível e em escala. Isso torna os métodos avançados de recuperação mais previsíveis em produção porque as restrições alinhadas à intenção são aplicadas primeiro e as decisões de roteamento são explícitas, em vez de implícitas.</p><h2>Como isso se relaciona com outras abordagens</h2><p>Algumas equipes usam modelos de incorporação aprimorados para capturar melhor a semântica do produto, o que pode melhorar substancialmente a qualidade da recuperação semântica. Outros utilizam abordagens de reclassificação, como <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">o Learning To Rank (LTR)</a>, para otimizar a ordenação dos resultados com base em engajamento ou sinais de negócio após a recuperação. Ambos são valiosos e frequentemente complementares. Embeddings melhores melhoram a correspondência de similaridade. A reclassificação melhora a ordem entre os candidatos recuperados.</p><p>A governança aborda uma camada diferente do problema: ela se situa antes da recuperação de dados. Ela decide qual estratégia de recuperação usar (por exemplo, lexical, semântica ou híbrida), quais restrições determinísticas são necessárias e quais consultas devem combinar várias políticas de negócios.</p><h2>O que um plano de controle governado permite</h2><p>Depois que uma camada de governança é implementada, o modelo operacional muda de forma fundamental. Consultas de busca críticas para a receita se tornam previsíveis. As equipes de negócios podem atualizar o comportamento de busca sem precisar esperar pelos ciclos de release da engenharia. E métodos avançados de recuperação, como a semântica e a híbrida, podem ser adotados de forma incremental, com roteamento e mecanismos de proteção, em vez de uma chave liga/desliga global.</p><p>O próximo post desta série explora como esse modelo operacional funciona na prática e por que ele pode ser tão importante quanto a tecnologia de retrieval que está por trás dele.</p><p>Se um comerciante precisar abrir um ticket do Jira e esperar por uma implantação para corrigir uma consulta crítica de receita, o gargalo não é o mecanismo; é o modelo operacional. A pesquisa moderna de comércio eletrônico precisa de uma forma de traduzir a intenção comercial em um comportamento de pesquisa controlado e auditável de forma rápida e segura, sem deixar de usar a recuperação avançada, que agrega valor mensurável.</p><h2>O que vem a seguir nesta série</h2><p>Os padrões explorados nesta série operam antes da recuperação: traduzindo a intenção comercial na estratégia de consulta correta antes do início da geração da consulta. No próximo post, passamos do problema técnico para o operacional: o que acontece quando equipes de negócios conseguem mudar o comportamento de busca sem uma implantação de engenharia, e por que a governança torna isso seguro.</p><h2>Coloque em prática o buscar governado de comércio eletrônico</h2><p>Gargalos de engenharia, lógica frágil da camada de aplicativos e resultados de busca imprevisíveis são problemas que a Elastic Services pode ajudar a resolver em contratos de serviços de comércio eletrônico corporativo. A arquitetura do plano de controle governado descrita nesta série foi construída pela Elastic Services Engineering.</p><p>Se sua equipe está gastando ciclos de engenharia traduzindo solicitações de merchandising em alterações de código, ou se o backlog de relevância de buscar nunca parece diminuir, podemos ajudá-lo a avaliar sua arquitetura atual e construir um caminho para uma buscar governada e editável pela área de negócios. Entre em contato com <a href="https://www.elastic.co/consulting">Elastic Services</a>.</p><h2>Participe da discussão</h2><p>Tem dúvidas sobre governança de buscar, estratégias de recuperação ou arquitetura de buscar para e-commerce? Participe da <a href="https://discuss.elastic.co/">conversa mais ampla da comunidade Elastic</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como criar aplicações de IA agentiva com Mastra e Elasticsearch]]></title>
            <description><![CDATA[Aprenda como construir aplicações de IA agentiva usando Mastra e Elasticsearch com um exemplo prático.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/build-agentic-ai-applications-mastra-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/build-agentic-ai-applications-mastra-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
            <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ab30baf2f908534840c5d71a46705773807baf54-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, vamos mostrar como usar o framework <a href="https://mastra.ai/">Mastra</a> TypeScript para criar aplicações agentivas que interagem com <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>.</p><p>Recentemente, contribuímos para o projeto open source <a href="https://github.com/mastra-ai/mastra">mastra-ai/mastra</a> adicionando suporte ao Elasticsearch como banco de dados vetorial. Com esse novo recurso, você pode usar o Elasticsearch nativamente no Mastra para armazenar embeddings. Além dos vetores, o Elasticsearch oferece um conjunto de recursos avançados para atender a todos os seus requisitos de engenharia de contexto. (por exemplo, <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">busca híbrida e reranking</a>).</p><p>Este artigo detalha a criação de um agente para implementar uma arquitetura de retrieval augmented generation (RAG) usando o Elasticsearch. Vamos apresentar um projeto de demonstração onde uma abordagem agentiva é usada para interagir com um corpus de dados de filmes de ficção científica armazenados no Elasticsearch. O projeto está disponível em <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a>.</p><h2>Mastra</h2><p>Mastra é um framework TypeScript para criar aplicações de IA com agentes.</p><p>A estrutura do projeto em Mastra é a seguinte:</p><pre><code>src/
├── mastra/
│   ├── agents/
│   │   └── weather-agent.ts
│   ├── tools/
│   │   └── weather-tool.ts
│   ├── workflows/
│   │   └── weather-workflow.ts
│   ├── scorers/
│   │   └── weather-scorer.ts
│   └── index.ts
├── .env.example
├── package.json
└── tsconfig.json</code></pre><p>No Mastra, você pode criar <a href="https://mastra.ai/docs/agents/overview">agentes</a>, <a href="https://mastra.ai/docs/agents/using-tools">ferramentas</a>, <a href="https://mastra.ai/docs/workflows/overview">fluxos de trabalho</a> e <a href="https://mastra.ai/docs/evals/overview">métricas</a>.</p><p>Um <strong>agente</strong> é uma classe que aceita uma mensagem na entrada e produz uma resposta como saída. Um agente pode usar ferramentas, grandes modelos de linguagem (LLMs) e uma memória (figura 1).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f6aca2dcc7fcc45d25e06681649be1b2b7eb6781-706x721.png" style="width: 100%;" /><p>As <strong>ferramentas</strong> de um agente permitem que ele interaja com o &quot;mundo externo&quot;, como se comunicar com uma API web ou realizar uma operação interna, como consultar o Elasticsearch. O componente de <strong>memória</strong> é essencial para armazenar o histórico das conversas, incluindo entradas e saídas anteriores. Esse contexto armazenado permite que o agente forneça respostas mais informadas e relevantes para futuras perguntas utilizando suas interações passadas.</p><p>Os <strong>fluxos de trabalho</strong> permitem que você defina sequências complexas de tarefas usando etapas claras e estruturadas, em vez de depender do raciocínio de um único agente (figura 2). Eles dão controle total sobre como as tarefas são divididas, como os dados circulam entre elas e o que é executado e quando. Os fluxos de trabalho são executados usando o mecanismo de execução integrado por padrão ou podem ser implantados em <a href="https://mastra.ai/docs/deployment/workflow-runners">executores de fluxo de trabalho</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0dce161cf7891207015dc87532b5b90df1822432-880x252.png" style="width: 100%;" /><p>No Mastra, você também pode definir métricas, que são testes automatizados para avaliar as saídas dos agentes usando métodos baseados em modelos, regras e estatísticas. Os avaliadores retornam <em>métricas</em>: valores numéricos (normalmente entre 0 e 1) que quantificam o quanto uma saída atende aos seus critérios de avaliação. Essas métricas permitem que você acompanhe objetivamente o desempenho, compare diferentes abordagens e identifique áreas de melhoria em seus sistemas de IA. Os avaliadores podem ser personalizados com seus próprios prompts e funções de métrica.</p><h2>Elasticsearch</h2><p>Para executar o projeto de demonstração, precisamos ter uma instância do Elasticsearch em execução. Você pode ativar um teste gratuito no <a href="https://www.elastic.co/cloud">Elastic Cloud</a> ou instalá-lo localmente usando o script <a href="https://github.com/elastic/start-local"><code>start-local</code></a>:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Isso instalará o Elasticsearch e o Kibana no seu computador e gerará uma chave API para ser usada na configuração da integração Mastra.</p><p>A chave API será mostrada como saída do comando anterior e armazenada em um arquivo <strong>.env</strong> na pasta elastic-start-local.</p><h2>Instalar e configurar a demonstração</h2><p>Criamos um repositório <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a> contendo o código-fonte do projeto de demonstração. O exemplo relatado no repositório ilustra como criar um agente no Mastra que implementa uma arquitetura RAG para recuperar documentos do Elasticsearch.</p><p>Fornecemos um conjunto de dados para a demonstração sobre filmes de ficção científica. Extraímos 500 filmes do conjunto de dados IMDb no <a href="https://www.kaggle.com/datasets/rajugc/imdb-movies-dataset-based-on-genre/versions/2?select=scifi.csv">Kaggle</a>.</p><p>O primeiro passo é instalar as dependências do projeto com npm, usando o seguinte comando:</p><pre><code>npm install</code></pre><p>Então precisamos configurar o arquivo <strong>.env</strong> que conterá as configurações. Podemos gerar esse arquivo copiando a estrutura do arquivo <strong>.env.example</strong>, usando o seguinte comando:</p><pre><code>cp .env.example .env</code></pre><p>Agora podemos editar o arquivo .env, adicionando as informações que faltam:</p><pre><code>OPENAI_API_KEY=
ELASTICSEARCH_URL=
ELASTICSEARCH_API_KEY=
ELASTICSEARCH_INDEX_NAME=scifi-movies</code></pre><p>O nome do índice do Elasticsearch é <strong><code>scifi-movies</code></strong>. Se quiser, pode mudar usando a variável de ambiente <code>ELASTICSEARCH_INDEX_NAME</code>.</p><p>Usamos a OpenAI como serviço de embeddings, o que significa que você precisa fornecer uma chave de API para a OpenAI na variável de ambiente <code>OPENAI_API_KEY</code>.</p><p>O modelo de embedding usado no exemplo é <a href="https://developers.openai.com/api/docs/models/text-embedding-3-small">openai/text-embedding-3-small</a>, com uma dimensão de embedding de 1.536.</p><p>Para gerar a resposta final, utilizamos o modelo <a href="https://developers.openai.com/api/docs/models/gpt-5-nano">openai/gpt-5-nano</a> para reduzir os custos.</p><p>A arquitetura RAG permite que você use um modelo LLM final menos potente (e normalmente mais barato) porque o trabalho pesado de fundamentar a resposta é feito pelo componente de recuperação (Elasticsearch, neste caso).</p><p>O LLM menor é responsável apenas por duas tarefas principais:</p><ul><li><strong>Reformulação/embedding da consulta:</strong> conversão da pergunta do usuário em linguagem natural em um vetor de embedding para busca semântica.</li><li><strong>Sintetização da resposta:</strong> pegar os fragmentos de contexto recuperados e altamente relevantes (documentos/filmes) e sintetizá-los em uma resposta coerente, final e legível por humanos, seguindo as instruções do prompt fornecido.</li></ul><p>Como o processo RAG <strong>fornece o contexto factual exato</strong> necessário para a resposta, o LLM final não precisa ser massivo ou altamente complexo, nem precisa possuir todo o conhecimento necessário dentro de seus próprios parâmetros (é aí que modelos grandes e caros se destacam). Basicamente, ele atua como um sofisticado resumidor e formatador de texto para o contexto fornecido pelo Elasticsearch, e não como uma base de conhecimento completa em si. Isso permite o uso de modelos como <code>gpt-5-nano</code> para otimização de custos e latência.</p><p>Após a configuração do arquivo .env, você pode fazer a ingestão dos filmes no Elasticsearch usando o seguinte comando:</p><pre><code>npx tsx src/utility/store.ts</code></pre><p>Você deve ver uma saída da seguinte forma:</p><pre><code>🚀 Starting ingestion of 500 movies from 500_scifi_movies.jsonl...
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 1/500 (0%) | ok:1 | fail:0 | chunks:1 | eta:19m 33s | current:Capricorn One
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 2/500 (0%) | ok:2 | fail:0 | chunks:2 | eta:10m 32s | current:Doghouse
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 3/500 (1%) | ok:3 | fail:0 | chunks:3 | eta:7m 33s | current:Dinocroc
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 4/500 (1%) | ok:4 | fail:0 | chunks:7 | eta:6m 10s | current:Back to the Future           
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 5/500 (1%) | ok:5 | fail:0 | chunks:9 | eta:5m 14s | current:The Projected Man            
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 6/500 (1%) | ok:6 | fail:0 | chunks:11 | eta:4m 41s | current:I, Robot
...
✅ Ingestion complete in 1m 46s. Success: 500, Failed: 0, Chunks: 693.</code></pre><p>O mapeamento do índice de filmes de ficção científica contém os seguintes campos:</p><ul><li><strong>embedding</strong>, dense_vector com dimensão de 1.536, similaridade cosseno.</li><li><strong>description</strong>, texto contendo a descrição do filme.</li><li><strong>director</strong>, texto contendo o nome do diretor.</li><li><strong>título</strong>, texto contendo o título do filme.</li></ul><p>Geramos os embeddings usando o título e a descrição. Como o título e a descrição são dois campos separados, a concatenação de ambos garante que o vetor de embedding resultante capture tanto a identidade específica e única (título) quanto o contexto rico e descritivo (descrição) do filme, resultando em buscas semânticas mais precisas e abrangentes. Essa entrada combinada oferece ao modelo de embedding uma representação mais adequada do conteúdo do documento para comparação de similaridade.</p><h2>Execute a demonstração</h2><p>Você pode executar a demonstração com o seguinte comando:</p><pre><code>npm run dev</code></pre><p>Esse comando iniciará uma aplicação web em <strong>localhost:4111</strong> para acessar o Mastra Studio (Figura 3).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1567e309df21a12bcf1dfef4429f82342549956c-1705x1079.png" style="width: 100%;" /><p>O <a href="https://mastra.ai/docs/getting-started/studio">Mastra Studio</a> oferece uma interface de usuário interativa para criar e testar seus agentes, além de uma REST API que expõe seu aplicativo Mastra como um serviço local. Isso permite que você comece a trabalhar imediatamente, sem se preocupar com integração.</p><p>Fornecemos um <strong>Agente Elasticsearch</strong> que utiliza <a href="https://mastra.ai/reference/tools/vector-query-tool">o createVectorQueryTool</a> da Mastra como ferramenta para executar busca semântica usando Elasticsearch. Esse agente utiliza a abordagem RAG para buscar documentos relevantes (ou seja, filmes) para responder à pergunta do usuário.</p><p>Este agente usa o seguinte prompt:</p><pre><code>You are a helpful assistant that answers questions based on the provided context.
Follow these steps for each response:

1. First, carefully analyze the retrieved context chunks and identify key information.
2. Break down your thinking process about how the retrieved information relates to the query.
3. Draw conclusions based only on the evidence in the retrieved context.
4. If the retrieved chunks don't contain enough information, explicitly state what's missing.

Format your response as:
THOUGHT PROCESS:
- Step 1: [Initial analysis of retrieved chunks]
- Step 2: [Reasoning based on chunks]

FINAL ANSWER:
[Your concise answer based on the retrieved context]

Important: When asked to answer a question, please base your answer only on the context provided in the tool. 
If the context doesn't contain enough information to fully answer the question, please state that explicitly and stop it.
Do not add more information than what is present in the retrieved chunks.
Remember: Explain how you're using the retrieved information to reach your conclusions.</code></pre><p>Se você clicar no menu <code>Mastra Studio &gt; Agents</code> e selecionar <strong>Agente Elasticsearch</strong>, pode testar o agente usando um sistema de chat. Por exemplo, você pode pedir informações sobre filmes de ficção científica com a seguinte pergunta:</p><p><em>Encontre 5 filmes ou séries de TV sobre OVNIs</em>.</p><p>Você notará que o agente executará a ferramenta vectorQueryTool. Você pode clicar na ferramenta invocada para visualizar a entrada e a saída. Ao final da execução, o LLM responderá à sua pergunta, considerando o contexto do índice de filmes de ficção científica do Elasticsearch (figura 4).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d9998d4f687984de98845dae52d1288166abf448-1344x1071.png" style="width: 100%;" /><p>O Mastra executa internamente os seguintes passos:</p><ol><li><strong>Conversão de vetor:</strong> A pergunta do usuário, <em>Encontre 5 filmes ou séries de TV sobre OVNIs,</em> é convertida em uma incorporação vetorial usando o modelo <code>openai/text-embedding-3-small</code> da OpenAI.</li><li><strong>Busca vetorial:</strong> este embedding é então usado para consultar o Elasticsearch por meio de uma busca vetorial.</li><li><strong>Recuperação do resultado:</strong> o Elasticsearch retorna um conjunto de 10 filmes altamente relevantes para a consulta (ou seja, aqueles cujos vetores estão mais próximos do vetor de consulta do usuário).</li><li><strong>Geração de respostas:</strong> os filmes recuperados e a pergunta original do usuário são enviados para o LLM, especificamente <code>openai/gpt-5-nano</code>. O LLM processa essas informações e gera uma resposta final, garantindo que o pedido do usuário por cinco resultados seja atendido.</li></ol><h2>O Agente Elasticsearch</h2><p>Aqui apresentamos o código-fonte do agente Elasticsearch.</p><pre><code>import { Agent } from "@mastra/core/agent";
import { ElasticSearchVector } from '@mastra/elasticsearch';
import { createVectorQueryTool } from '@mastra/rag';
import { ModelRouterEmbeddingModel } from "@mastra/core/llm";
import { Memory } from "@mastra/memory";

const es_url = process.env.ELASTICSEARCH_URL;
const es_apikey = process.env.ELASTICSEARCH_API_KEY;
const es_index_name = process.env.ELASTICSEARCH_INDEX_NAME;
const prompt = 'insert here the previous prompt';

const esVector = new ElasticSearchVector({
  id: 'elasticsearch-vector',
  url: es_url,
  auth: {
    apiKey : es_apikey
  }
});

const vectorQueryTool = createVectorQueryTool({
  vectorStore: esVector,
  indexName: es_index_name,
  model: new ModelRouterEmbeddingModel("openai/text-embedding-3-small")
});

export const elasticsearchAgent = new Agent({
  id: "elasticsearch-agent",
  name: "Elasticsearch Agent",
  instructions: prompt,
  model: 'openai/gpt-5-nano',
  tools: { vectorQueryTool },
  memory: new Memory(),
});</code></pre><p>O <strong>vectorQueryTool</strong> é a ferramenta que é invocada para implementar a parte de recuperação do exemplo RAG. Ele utiliza a implementação <a href="https://mastra.ai/reference/vectors/elasticsearch">ElasticSearchVector</a> que a Elastic contribuiu para o Mastra.</p><p>O agente é um objeto da classe agent que utiliza o vectorQueryTool, o prompt e uma memória. Como você pode ver, o código que precisamos colocar em prática para conectar o Elasticsearch a um agente é mínimo.</p><h2>Conclusão</h2><p>Este artigo demonstrou a simplicidade e o poder de integrar o Elasticsearch ao framework Mastra para criar aplicações sofisticadas de IA agentiva. Especificamente, detalhamos a criação de um agente RAG capaz de realizar busca semântica em um corpus de dados de filmes de ficção científica indexados no Elasticsearch.</p><p>O principal aprendizado é a contribuição direta da Elastic para o projeto open source Mastra, fornecendo suporte nativo para o Elasticsearch como um repositório vetorial. Essa integração reduz significativamente a barreira de entrada, como demonstra o código-fonte do <strong>Elasticsearch Agent</strong>. Usando o <code>ElasticSearchVector</code> e <code>createVectorQueryTool</code>, a configuração completa para conectar o Elasticsearch ao seu agente exige apenas algumas linhas de código de configuração.</p><p>O Elasticsearch oferece vários recursos avançados para aumentar a relevância dos resultados. Por exemplo, a <a href="https://www.elastic.co/elasticsearch/hybrid-search">busca híbrida</a> aumenta significativamente a precisão ao combinar a busca lexical com a busca vetorial. Outro recurso interessante é a reclassificação usando os <a href="https://www.elastic.co/search-labs/tutorials/jina-tutorial/jina-reranker-v3">modelos Jina</a> mais recentes, que podem ser aplicados ao final da busca híbrida. Para saber mais sobre essas técnicas, consulte os seguintes artigos do Elasticsearch Labs:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">Busca híbrida do Elasticsearch</a> por Valentin Crettaz</li><li><a href="https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide">Uma introdução aos modelos Jina, sua funcionalidade e seus usos no Elasticsearch</a> por Scott Martens</li></ul><p>Também incentivamos você a explorar o exemplo fornecido e começar a construir seus próprios agentes baseados em dados com Mastra e Elasticsearch. Para mais informações sobre o Mastra, você pode consultar a documentação oficial <a href="https://mastra.ai/docs">aqui</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Monitorando as visualizações do dashboard do Kibana com o Elastic Workflows]]></title>
            <description><![CDATA[Aprenda a usar o Elastic Workflows para coletar métricas de visualização do dashboard do Kibana a cada 30 minutos e indexá-las no Elasticsearch, para que você possa criar análises e visualizações personalizadas com base em seus próprios dados.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/monitor-kibana-dashboard-views-elastic-workflows</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/monitor-kibana-dashboard-views-elastic-workflows</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
            <pubDate>Fri, 03 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ed94ce00962287b5507f45c92ecb60fdcbf2718-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O <a href="https://www.elastic.co/kibana">Kibana</a> rastreia quantas vezes cada dashboard é visualizado, mas esses dados não são expostos nativamente em nenhum dashboard integrado. Neste artigo, vamos usar o <strong>Elastic Workflows</strong> para coletar automaticamente esses dados a cada 30 minutos e indexá-los no Elasticsearch, para que possamos criar nossa própria analítica sobre eles.</p><p>O <a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic Workflows</a> é um mecanismo de automação integrado dentro do Kibana que permite definir processos de várias etapas usando uma simples configuração YAML. Cada fluxo de trabalho pode ser acionado em um cronograma ou evento ou como uma ferramenta no <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Elastic Agent Builder</a>, e cada etapa pode chamar APIs do Kibana, consultar o Elasticsearch ou transformar dados.</p><p>Vamos usar as contagens de visualização de dashboards como um exemplo concreto, mas o mesmo padrão se aplica a qualquer métrica exposta pela API de objetos salvos do Kibana.</p><h2>Pré-requisitos</h2><ul><li><a href="https://www.elastic.co/cloud">Elastic Cloud</a> ou cluster <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed">autogerenciado </a>executando a versão 9.3</li><li><a href="https://www.elastic.co/docs/explore-analyze/workflows/get-started#workflows-prerequisites">Fluxos de trabalho ativados</a> (Configurações avançadas)</li></ul><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p>Antes de construir qualquer coisa, vamos entender quais dados temos. O Kibana armazena a maior parte de sua configuração e metadados como <a href="https://www.elastic.co/docs/api/doc/kibana/group/endpoint-saved-objects">objetos salvos</a> em um índice interno dedicado. Uma das coisas que o Kibana monitora dessa forma são as contagens de visualizações do dashboard, usando um tipo especial de objeto salvo chamado contadores de uso. Você pode consultá-los diretamente pelas Ferramentas de Desenvolvimento:</p><pre><code>GET kbn:/api/saved_objects/_find?type=usage-counter&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"&per_page=10000</code></pre><p>A resposta tem aparência semelhante a esta:</p><pre><code>{
  "page": 1,
  "per_page": 10000,
  "total": 1,
  "saved_objects": [
    {
      "type": "usage-counter",
      "id": "dashboard:346f3c64-ebca-484d-9d57-ec600067d596:viewed:server:20260310",
      "attributes": {
        "domainId": "dashboard",
        "counterName": "346f3c64-ebca-484d-9d57-ec600067d596",
        "counterType": "viewed",
        "source": "server",
        "count": 1
      },
      ...
    }
  ]</code></pre><p>O campo <code>counterName</code> é o ID do dashboard, e <code>count</code> é a contagem cumulativa de visualizações para aquele dashboard naquele dia específico. Kibana cria um objeto contador por dashboard por dia; você pode ver o sufixo de data no ID do objeto (... visualizado:servidor:20260310). A contagem cresce ao longo do dia à medida que os usuários abrem o dashboard.</p><p>Em vez de replicar esse modelo de documento diário em nosso índice, criaremos um documento por execução de fluxo de trabalho. Cada documento registra quantas visualizações aquele dashboard acumulou no dia no momento da captura.</p><h2>Passo 2: criar o índice de destino</h2><p>Precisamos de um índice para armazenar os snapshots da vista do nosso dashboard. O comando a seguir cria com mapeamentos explícitos para que possamos agregar e visualizar depois. Execute isso nas ferramentas de desenvolvimento:</p><pre><code>PUT dashboard-views
{
  "mappings": {
    "properties": {
      "captured_at": {
        "type": "date"
      },
      "dashboard_id": {
        "type": "keyword"
      },
      "dashboard_name": {
        "type": "keyword"
      },
      "view_count": {
        "type": "integer"
      }
    }
  }
}</code></pre><p>O uso de mapeamentos <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/keyword"><code>keyword</code></a> para IDs e nomes permite <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agregações</a>. Usar <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/number"><code>integer</code></a> para <code>view_count</code> é um padrão seguro, já que o Kibana reinicia o contador diariamente e atingir o limite de 32 bits (mais de 2 bilhões de visualizações em um único dia) não é uma preocupação realista. Ainda permite operações numéricas, como <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-max-aggregation"><code>max</code></a>, <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-avg-aggregation"><code>avg</code></a> e <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-metrics-min-aggregation"><code>min</code></a>, entre outras.</p><h2>Passo 3: Crie o fluxo de trabalho</h2><p>Acesse <strong>Stack Management &gt; Fluxo de trabalho &gt; Novo Fluxo de Trabalho</strong> e cole a seguinte configuração YAML do fluxo de trabalho:</p><pre><code>name: dashboard-views-ingestion
triggers:
  - type: scheduled
    with:
      every: 30m

steps:
  - name: fetch_dashboard_views
    type: kibana.request
    with:
      method: GET
      path: >-
        /api/saved_objects/_find?type=usage-counter&per_page=10000&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"

  - name: index_each_dashboard
    type: foreach
    foreach: "{{ steps.fetch_dashboard_views.output.saved_objects }}"
    steps:
      - name: fetch_dashboard_name
        type: kibana.request
        with:
          method: GET
          path: /api/saved_objects/dashboard/{{ foreach.item.attributes.counterName }}
        on-failure:
          continue: true

      - name: index_doc
        type: elasticsearch.request
        with:
          method: POST
          path: /dashboard-views/_doc
          body:
            dashboard_id: "{{ foreach.item.attributes.counterName }}"
            dashboard_name: "{{ steps.fetch_dashboard_name.output.attributes.title }}"
            view_count: "${{ foreach.item.attributes.count | plus: 0 }}"
            captured_at: "{{ execution.startedAt | date: '%Y-%m-%dT%H:%M:%SZ' }}"</code></pre><p>Na próxima seção, vamos analisar o fluxo de trabalho passo a passo.</p><h3>Como funciona o fluxo de trabalho</h3><h4>Gatilhos</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5670991d65c64ee833924225c2d375a1be868b13-325x162.png" style="width: 100%;" /><p>O fluxo de trabalho é executado com um gatilho programado a cada 30 minutos. Isso nos fornece dados de séries temporais sem sobrecarregar a API.</p><h4>buscar_visualizações_do_painel</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66eaec147c3d01c524c67cf1c7f663ac56a3259d-812x215.png" style="width: 100%;" /><p>Usa <code>kibana.request</code> para chamar a API de objetos salvos do Kibana. Não é necessário configurar autenticação: o motor de fluxo de trabalho anexa automaticamente os cabeçalhos corretos com base no contexto de execução.</p><h4>index_each_dashboard (foreach)</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aad45e8aed8dc81ded6260cd6199ff78dcffe3b4-1892x290.png" style="width: 100%;" /><p>Itera sobre o array <a href="https://www.elastic.co/docs/api/doc/kibana/group/endpoint-saved-objects"><code>saved_objects</code></a> retornado pela etapa anterior. O item atual em cada iteração está disponível como <code>foreach.item</code>. Dentro do loop, executamos duas etapas aninhadas para cada dashboard.</p><p><strong>1. <code>fetch_dashboard_name</code>:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/db21ec5094b743018b9cd66c5052681f14c7d7e3-1999x431.png" style="width: 100%;" /><p>Resolve o título do dashboard legível por humanos chamando <code>GET /api/saved_objects/dashboard/{id}</code>. Adicionamos <code>on-failure: continue: true</code> para que, se um dashboard for excluído mas ainda tiver contadores de visualização, o loop continue em vez de falhar toda a execução.</p><p><strong>2. <code>index_doc</code>:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f49dd0c9f0817bb1e1e5d9f4a2b05d13ef331054-1999x626.png" style="width: 100%;" /><p>Indexa cada documento usando <code>POST /dashboard-views/_doc</code> (sem um ID explícito), o que permite que o Elasticsearch gere IDs automaticamente. Isso cria um novo documento a cada execução, construindo um histórico de contagens de visualizações ao longo do tempo, em vez de sobrescrever o snapshot anterior.</p><p>Duas coisas que valem a pena notar:</p><ul><li>O campo <code>captured_at</code> usa o filtro de data para formatar o carimbo de data/hora como <a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a>. Sem isso, o valor aparece como uma string de data em JavaScript, como <code>Tue Mar 10 2026 05:03:47 GMT+0000</code>, que o Elasticsearch não mapeia como data.</li><li>O <code>view_count</code> usa a sintaxe <code>${{ }}</code> com <code>| plus: 0</code> para preservar o tipo numérico. Usar <code>{{ }}</code> o renderizaria como uma string, o que impediria operações matemáticas no dashboard.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6d48c8784d5df6192e8b5175e69dbab5098194bc-919x774.png" style="width: 100%;" /><p><em>A UI permite que você depure cada uma das etapas do fluxo de trabalho.</em></p><h2>Etapa 4: Crie o dashboard de estatísticas</h2><p>Depois que o fluxo de trabalho for executado algumas vezes e os dados forem coletados, crie um novo dashboard no Kibana usando a Data view dashboard-views.</p><p>Alguns painéis para começar:</p><ul><li><strong>Principais dashboards por visualizações:</strong> use um <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/bar-charts"><strong>gráfico de barras</strong></a> com <code>dashboard_name</code> no eixo X e <code>last_value(view_count)</code> no eixo Y. Isso mostra a contagem diária atual de visualizações por dashboard.</li><li><strong>Visualizações ao longo do tempo:</strong> use um <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/line-charts"><strong>gráfico de linhas</strong></a> com <code>captured_at</code> no eixo X e <code>last_value(view_count)</code> no eixo Y, dividido por <code>dashboard_name</code>. Como cada execução adiciona um novo documento, use o último valor para obter a contagem de picos por buckets, em vez de somar duplicados.</li><li><strong>Snapshot atual:</strong> use uma <a href="https://www.elastic.co/docs/explore-analyze/visualize/charts/tables"><strong>tabela de dados</strong></a> com os <code>captured_at</code> mais recentes para mostrar as contagens de visualizações mais recentes em todos os dashboards.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e245b95f67daf76a2aaf4cb9df2c75ef4cfef582-1462x747.png" style="width: 100%;" /><p>Como cada fluxo de trabalho cria um novo documento, você pode filtrar por faixa de tempo para analisar a atividade em períodos específicos, comparar semana a semana ou criar alertas quando um dashboard cair abaixo de um limite de visualização.</p><h2><strong>Conclusão</strong></h2><p>O Elastic Workflows é uma boa opção para esse tipo de coleta periódica de dados porque tanto a fonte (Kibana API) quanto o destino (Elasticsearch) são nativos, o que significa zero gerenciamento de credenciais. O motor de fluxo de trabalho lida automaticamente com autenticação para <code>kibana.request</code> e <code>elasticsearch.request</code> etapas, então a única coisa que você escreve é a lógica.</p><h2><strong>Recursos</strong></h2><ul><li><a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic Workflows</a></li><li><a href="https://www.elastic.co/docs/api/doc/kibana/">API do Kibana</a></li></ul>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/tsds-ilm-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/tsds-ilm-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Bret Wortman]]></dc:creator>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e4c46e4a6f746d9c845857e80de036f5d51cd4e7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Recentemente, migrei o cluster de métricas de um cliente de &quot;tudo na camada ativa&quot; 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>Índices de suporte aceitam apenas documentos dentro de uma janela de tempo específica.</li><li>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.</li></ul><p>Regra de design:</p><pre><code>warm_min_age > rollover_max_age + maximum_expected_lateness</code></pre><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>Um registro de data e hora.</li><li>Campos dimensionais que identificam a série de tempo.</li><li>Campos métricos representando valores medidos.</li></ul><p>Alguns exemplos:</p><ul><li>Uso da CPU por host.</li><li>Solicitar latência por serviço.</li><li>Leituras de temperatura por sensor.</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><pre><code>host.name
service.name
container.id</code></pre><p>Definimos eles em mapeamentos com:</p><pre><code>time_series_dimension: true</code></pre><h3>Métricas</h3><p>Métricas representam valores numéricos e são definidas usando:</p><pre><code>time_series_metric</code></pre><p>Tipos comuns de métricas:</p><ul><li>Indicador: Valores que sobem e descem.</li><li>Contador: valores que aumentam até serem reiniciados.</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>Compressão.</li><li>Local da consulta.</li><li>Desempenho de agregações.</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><pre><code>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"
         }
       ]
     }</code></pre><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>Dados em tempo real → índice mais recente.</li><li>Dados tardios → índice anterior cobrindo esse intervalo de tempo.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31af2bb3b3dc24db8342e791e1db77a44659ba7a-1589x502.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32a103b95b20e31615c214271e27811a7ee315ae-1999x691.png" style="width: 100%;" /><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><pre><code>time_until_readonly > maximum_expected_lateness</code></pre><p>Como o ILM mede o tempo de existência a partir do rollover, a regra operacional passa a ser:</p><pre><code>warm_or_cold_min_age > rollover_max_age + maximum_expected_lateness</code></pre><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><pre><code>GET metrics-myapp::failures/_search</code></pre><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>Quedas repentinas na taxa de indexação.</li><li>Picos nos documentos rejeitados.</li><li>Um número crescente de entradas de lojas que falham.</li><li>Diferenças de incompatibilidade entre entradas e saídas do pipeline contagem.</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>Atraso normal na ingestão de dados.</li><li>Pior caso de atraso durante os incidentes.</li><li>Atrasos causados por pipelines em lote.</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><pre><code>GET _data_stream/<your-stream></code></pre><p>Analise:</p><ul><li><code>time_series.start_time</code></li><li><code>time_series.end_time</code></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><code>warm_min_age &gt; rollover_max_age + maximum_expected_lateness</code></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>Armazenamento de falhas (recomendado no Elasticsearch 9.1+).</li><li>Fila de dead letter do Logstash.</li><li>Índice de contingência para chegadas tardias.</li><li>Aceitar a perda limitada de dados.</li></ul><h3><strong>5. Monitorar a saúde da ingestão</strong></h3><p>Adicionar alertas para:</p><ul><li>A taxa de indexação cai.</li><li>Documentos rejeitados.</li><li>Crescimento do armazenamento de falhas.</li><li>Desajustes de entrada/saída do pipeline.</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>Os índices devem permanecer graváveis tempo suficiente para aceitar dados tardios.</li><li>Os pipelines de ingestão devem lidar com documentos rejeitados com segurança.</li></ul><p>A regra fundamental a lembrar é:</p><pre><code>warm_min_age > rollover_max_age + maximum_expected_lateness</code></pre><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>
        </item>
        <item>
            <title><![CDATA[Do LINQ para ES|QL: escreva C# e consulte o Elasticsearch]]></title>
            <description><![CDATA[Explorando o novo provedor LINQ para ES|QL no cliente Elasticsearch .NET, que permite escrever código C# que é automaticamente traduzido para consultas ES|QL.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/linq-esql-c-elasticsearch-net-client</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/linq-esql-c-elasticsearch-net-client</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Florian Bernd, Martijn Laarman]]></dc:creator>
            <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><pre><code>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; }
}</code></pre><p>Com o tipo definido, uma consulta fica assim:</p><pre><code>var minPrice = 100.0;
var brand = "TechCorp";

await foreach (var product in client.Esql.QueryAsync<Product>(q => q
    .From("products")
    .Where(p => p.InStock && p.Price >= minPrice && p.Brand == brand)
    .OrderByDescending(p => p.Price)
    .Take(10)))
{
    Console.WriteLine($"{product.Name}: ${product.Price}");
}</code></pre><p>O provedor traduz isso para o seguinte ES|QL:</p><pre><code>FROM products
| WHERE (in_stock == true AND price_usd >= ?minPrice AND brand == ?brand)
| SORT price_usd DESC
| LIMIT 10</code></pre><p>Há alguns detalhes a serem observados:</p><ul><li><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.</li><li><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.</li><li><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.</li></ul><p>Você também pode inspecionar a consulta gerada e seus parâmetros sem executá-la:</p><pre><code>var query = client.Esql.CreateQuery<Product>()
    .Where(p => p.InStock && p.Price >= minPrice && p.Brand == brand)
    .OrderByDescending(p => p.Price)
    .Take(10);

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

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

var parameters = query.GetParameters();
// { "minPrice": 100.0, "brand": "TechCorp" }</code></pre><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><pre><code>// IEnumerable: the lambda is a compiled delegate
IEnumerable<Product> local = products.Where(p => p.Price > 100);

// IQueryable: the lambda is an expression tree, a data structure
IQueryable<Product> remote = queryable.Where(p => p.Price > 100);</code></pre><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f864cd18a390831f8d28503a29b5835efb1842f7-1000x720.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2a2c772b63d720f61fc9a28b2f85668fa2db8d38-1999x1036.png" style="width: 100%;" /><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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Visitante</td><td style="border: 1px solid #ccc; padding: 4px;">Traduz</td><td style="border: 1px solid #ccc; padding: 4px;">Para</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">WhereClauseVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.Where(predicate)</td><td style="border: 1px solid #ccc; padding: 4px;">Condição ONDE</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">SelectProjectionVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.Select(selector)</td><td style="border: 1px solid #ccc; padding: 4px;">EVAL + KEEP + RENOMEAR</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">GroupByVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.GroupBy().Select()</td><td style="border: 1px solid #ccc; padding: 4px;">ESTATÍSTICAS ... POR</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OrderByVisitor</td><td style="border: 1px solid #ccc; padding: 4px;">.OrderBy() / .ThenBy()</td><td style="border: 1px solid #ccc; padding: 4px;">Campo SORT [ASC\|DESC]</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">EsqlFunctionTranslator</td><td style="border: 1px solid #ccc; padding: 4px;">EsqlFunctions.*, Math.*, métodos de string</td><td style="border: 1px solid #ccc; padding: 4px;">80+ funções ES|QL</td>
                    </tr>
              </tbody>
            </table>
          <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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2adc349b6cf655b96b7b3e826a134e8a17fe42fd-1999x1036.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/41b8aae860ecdc2480edcb1c1d4cc9b03cfb78c9-1999x1036.png" style="width: 100%;" /><p><em>Arquitetura de pacotes.</em><br/><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><pre><code>// Translation-only: no Elasticsearch connection needed
var provider = new EsqlQueryProvider();
var query = new EsqlQueryable<Product>(provider)
    .From("products")
    .Where(p => p.InStock)
    .OrderByDescending(p => p.Price);

Console.WriteLine(query.ToEsqlString());
// FROM products | WHERE in_stock == true | SORT price_usd DESC</code></pre><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><pre><code>var stats = client.Esql.Query<Product, object>(q => q
    .GroupBy(p => p.Brand)
    .Select(g => new
    {
        Brand = g.Key,
        Count = g.Count(),
        AvgPrice = g.Average(p => p.Price),
        MaxPrice = g.Max(p => p.Price)
    }));

// -> FROM products | STATS COUNT(*), AVG(price_usd), MAX(price_usd) BY brand</code></pre><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><pre><code>var query = client.Esql.CreateQuery<Product>()
    .Select(p => new { ProductName = p.Name, p.Price, p.InStock });

// -> FROM products | KEEP name, price_usd, in_stock | RENAME name AS ProductName</code></pre><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><pre><code>.Where(p => p.Name.Contains("Pro"))       // -> WHERE name LIKE "*Pro*"
.Where(p => EsqlFunctions.CidrMatch(      // -> WHERE CIDR_MATCH(ip, "10.0.0.0/8")
    p.IpAddress, "10.0.0.0/8"))</code></pre><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><pre><code>var enriched = client.Esql.Query<Product, object>(q => q
    .LookupJoin<Product, CategoryLookup, string, object>(
        "category-lookup-index",
        product => product.Id,
        category => category.CategoryId,
        (product, category) => new { product.Name, category!.CategoryLabel }));</code></pre><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><pre><code>var results = client.Esql.Query<Product>(q => q
    .Where(p => p.InStock)
    .RawEsql("| EVAL discounted = price_usd * 0.9"));</code></pre><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><pre><code>await using var asyncQuery = await client.Esql.SubmitAsyncQueryAsync<Product>(
    q => q.Where(p => 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);</code></pre><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><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x branch)</li><li><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (8.x branch)</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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Método</td><td style="border: 1px solid #ccc; padding: 4px;">Returns</td><td style="border: 1px solid #ccc; padding: 4px;">Caso de uso</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Query<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">IEnumerable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Execução síncrona</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">QueryAsync<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">IAsyncEnumerable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Streaming assíncrono</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">CreateQuery<T>()</td><td style="border: 1px solid #ccc; padding: 4px;">IEsqlQueryable<T></td><td style="border: 1px solid #ccc; padding: 4px;">Composição avançada e inspeção</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">SubmitAsyncQueryAsync<T>(...)</td><td style="border: 1px solid #ccc; padding: 4px;">EsqlAsyncQuery<T></td><td style="border: 1px solid #ccc; padding: 4px;">Consultas de longa duração no servidor</td>
                    </tr>
              </tbody>
            </table>
          <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>
        </item>
        <item>
            <title><![CDATA[Criando um servidor MCP do Elasticsearch com TypeScript]]></title>
            <description><![CDATA[Saiba como criar servidor MCP do Elasticsearch com TypeScript e Claude Desktop.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elastic-mcp-server-typescript-claude</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elastic-mcp-server-typescript-claude</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0bb24c05fff391f42070c2883182ea6fe9cb9680-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Ao trabalhar com grandes bases de conhecimento no Elasticsearch, encontrar informações é apenas metade da batalha. Engenheiros precisam sintetizar resultados de múltiplos documentos, gerar resumos e rastrear respostas até as fontes. Para isso, o Protocolo de Contexto do Modelo (MCP) oferece uma maneira padronizada de conectar o Elasticsearch a aplicativos baseados em grandes modelos de linguagem (LLM). Embora a Elastic ofereça soluções oficiais, como o Elastic Agent Builder (que inclui um <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">endpoint MCP</a> entre os recursos), a criação de um servidor MCP personalizado oferece controle total sobre a lógica de busca, a formatação dos resultados e como o conteúdo recuperado é passado para um LLM para síntese, resumos e citações.</p><p>Neste artigo, exploraremos os benefícios de criar um servidor MCP do Elasticsearch personalizado e mostraremos como criar um servidor em TypeScript que conecte o Elasticsearch a aplicativos com LLM.</p><h2>Por que criar um servidor MCP do Elasticsearch personalizado?</h2><p>A Elastic oferece algumas alternativas para <a href="https://www.elastic.co/docs/solutions/search/mcp">servidores MCP</a>:</p><ul><li><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">Servidor MCP do Elastic Agent Builder para Elasticsearch 9.2+</a></li><li><a href="https://github.com/elastic/mcp-server-elasticsearch?tab=readme-ov-file#elasticsearch-mcp-server">Servidor MCP do Elasticsearch para versões mais antigas (Python)</a></li></ul><p>Se você precisar de mais controle sobre como o servidor MCP interage com o Elasticsearch, a criação do seu próprio servidor personalizado oferece a flexibilidade de adaptá-lo exatamente às suas necessidades. Por exemplo, o endpoint MCP do Agent Builder é limitado a consultas em Elasticsearch Query Language (ES|QL), enquanto um servidor personalizado permite usar o Query DSL completo. Você também tem controle sobre como os resultados são formatados antes de serem passados para o LLM e pode integrar etapas adicionais de processamento, como o resumo com tecnologia OpenAI que implementaremos neste tutorial.</p><p>Ao final deste artigo, você terá um servidor MCP no TypeScript que busca informações armazenadas em um índice do Elasticsearch, resume essas informações e fornece citações. Usaremos o Elasticsearch para recuperação, o modelo <code>gpt-4o-mini</code> da OpenAI para resumir e gerar citações, e o Claude Desktop como cliente MCP e UI para receber consultas dos usuários e dar respostas. O resultado é um assistente de conhecimento interno que ajuda os engenheiros a entender e sintetizar as práticas recomendadas nos documentos técnicos de sua organização.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec5771a874cf9740d4cac6888622cbe8cd6aede7-1999x1133.png" style="width: 100%;" /><h2>Pré-requisitos:</h2><ul><li>Node.js 20 +</li><li>Elasticsearch</li><li>Chave de API da OpenAI</li><li>Claude Desktop</li></ul><h3>O que é MCP?</h3><p><a href="https://www.elastic.co/what-is/mcp">O MCP</a> é um padrão aberto, criado pela <a href="https://www.anthropic.com/news/model-context-protocol">Anthropic</a>, que oferece conexões seguras e bidirecionais entre LLMs e sistemas externos, como o Elasticsearch. Você pode ler mais sobre a situação atual do MCP <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">neste artigo</a>.</p><p>O cenário de MCP está <a href="https://www.elastic.co/search-labs/blog/mcp-current-state#mcp-project-updates:-transport,-elicitation,-and-structured-tooling">em constante evolução</a>, com servidores disponíveis para uma ampla gama de casos de uso. Além disso, é fácil criar seu próprio servidor MCP personalizado, como mostraremos neste artigo.</p><h3>Clientes do MCP</h3><p>Há uma longa <a href="https://modelcontextprotocol.io/clients">lista de clientes MCP disponíveis</a>, cada um com as próprias características e limitações. Por simplicidade e popularidade, usaremos o <a href="https://claude.ai/download">Claude Desktop</a> como nosso cliente MCP. Ele servirá como interface de chat na qual os usuários poderão fazer perguntas em linguagem natural e que invocará automaticamente as ferramentas expostas pelo nosso servidor MCP para buscar documentos e gerar resumos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66eb0b11473347b6cf2d85718251eeac38d6249d-1999x1491.png" style="width: 100%;" /><h2>Criando um servidor MCP do Elasticsearch</h2><p>Usando o <a href="https://github.com/modelcontextprotocol/typescript-sdk">TypeScript SDK</a>, podemos criar um servidor que entende como consultar nossos dados do Elasticsearch com base em uma entrada de consulta do usuário.</p><p>Aqui estão os passos deste artigo para integrar o servidor MCP do Elasticsearch com o cliente Claude Desktop:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#configure-mcp-server-for-elasticsearch">Configure o servidor MCP para o Elasticsearch.</a></li><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#load-the-mcp-server-into-claude-desktop">Carregue o servidor MCP no Claude Desktop.</a></li><li><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#test-it-out">Faça o teste.</a></li></ol><h3>Configure o servidor MCP para o Elasticsearch</h3><p>Para começar, vamos iniciar uma aplicação de nó:</p><pre><code>npm init -y</code></pre><p>Isso criará um arquivo <code>package.json</code> e, com ele, poderemos começar a instalar as dependências necessárias para essa aplicação.</p><pre><code>npm install @elastic/elasticsearch @modelcontextprotocol/sdk openai zod && npm install --save-dev ts-node @types/node typescript</code></pre><ul><li><strong>@elastic/elasticsearch</strong> nos dará acesso à biblioteca Elasticsearch Node.js.</li><li><strong>@modelcontextprotocol/sdk</strong> fornece as ferramentas de núcleo para criar e gerenciar um servidor MCP, registrar ferramentas e lidar com a comunicação com clientes MCP.</li><li><strong>openai</strong> permite a interação com modelos OpenAI para gerar resumos ou respostas em linguagem natural.</li><li><strong><a href="https://zod.dev/">zod</a> </strong>ajuda a definir e validar esquemas estruturados para dados de entrada e saída em cada ferramenta.</li></ul><p><code>ts-node</code>, <code>@types/node</code>, e <code>typescript</code> serão usados durante o desenvolvimento para digitar o código e compilar os scripts.</p><h4>Configurar o conjunto de dados</h4><p>Para fornecer os dados que o Claude Desktop pode consultar usando nosso servidor MCP, usaremos um <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/dataset.json">conjunto de dados fictício de base de conhecimento interna</a>. Veja como será um documento desse conjunto de dados:</p><pre><code>{
    "id": 5,
    "title": "Logging Standards for Microservices",
    "content": "Consistent logging across microservices helps with debugging and tracing. Use structured JSON logs and include request IDs and timestamps. Avoid logging sensitive information. Centralize logs in Elasticsearch or a similar system. Configure log rotation to prevent storage issues and ensure logs are searchable for at least 30 days.",
    "tags": ["logging", "microservices", "standards"]
}</code></pre><p>Para ingerir os dados, preparamos um script que cria um índice no Elasticsearch e carrega o conjunto de dados nele. Você pode encontrá-lo <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/setup.ts">aqui</a>.</p><h4>Servidor MCP</h4><p>Crie um arquivo chamado <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/index.ts"><code>index.ts</code></a> e adicione o seguinte código para importar as dependências e lidar com as variáveis de ambiente:</p><pre><code>// index.ts
import { z } from "zod";
import { Client } from "@elastic/elasticsearch";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import OpenAI from "openai";

const ELASTICSEARCH_ENDPOINT =
  process.env.ELASTICSEARCH_ENDPOINT ?? "http://localhost:9200";
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY ?? "";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const INDEX = "documents";</code></pre><p>Além disso, vamos inicializar os clientes para lidar com as chamadas do Elasticsearch e do OpenAI:</p><pre><code>const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

const _client = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
});</code></pre><p>Para tornar nossa implementação mais robusta e garantir entrada e saída estruturadas, definiremos esquemas usando <a href="https://zod.dev/"><code>zod</code></a>. Isso nos permite validar dados em tempo de execução, detectar erros com antecedência e facilitar o processamento de forma programática das respostas da ferramenta:</p><pre><code>const DocumentSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
});

const SearchResultSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
  score: z.number(),
});

type Document = z.infer<typeof DocumentSchema>;
type SearchResult = z.infer<typeof SearchResultSchema>;</code></pre><p>Saiba mais sobre saídas estruturadas <a href="https://www.elastic.co/search-labs/blog/structured-outputs-elasticsearch-guide">aqui</a>.</p><p>Agora vamos inicializar o servidor MCP:</p><pre><code>const server = new McpServer({
  name: "Elasticsearch RAG MCP",
  description:
    "A RAG server using Elasticsearch. Provides tools for document search, result summarization, and source citation.",
  version: "1.0.0",
});</code></pre><h4>Definição das ferramentas MCP</h4><p>Com tudo configurado, podemos começar a escrever as ferramentas que serão expostas pelo nosso servidor MCP. Esse servidor expõe duas ferramentas:</p><ul><li><strong><code>search_docs</code>: </strong>Busca por documentos no Elasticsearch usando busca de texto completo.</li><li><strong><code>summarize_and_cite</code>:</strong> Resume e sintetiza informações de documentos previamente recuperados para responder a uma pergunta do usuário. Essa ferramenta também adiciona citações que referenciam os documentos fonte.</li></ul><p>Juntas, essas ferramentas formam um fluxo de trabalho simples de &quot;recuperar e resumir&quot;, em que uma ferramenta busca documentos relevantes e a outra usa esses documentos para gerar uma resposta resumida e citada.</p><h4>Formato de resposta da ferramenta</h4><p>Cada ferramenta pode aceitar parâmetros de entrada arbitrários, mas deve responder com a seguinte estrutura:</p><ul><li><strong>Conteúdo:</strong> esta é a resposta da ferramenta em um formato não estruturado. Este campo geralmente é usado para retornar texto, imagens, áudio, links ou embeddings. Para esta aplicação, ele será usado para retornar texto formatado com as informações geradas pelas ferramentas.</li><li><strong>structuredContent: </strong>Esse é um retorno opcional usado para fornecer os resultados de cada ferramenta em um formato estruturado. É útil para fins programáticos. Embora não seja usado neste servidor MCP, pode ser útil caso você queira desenvolver outras ferramentas ou processar os resultados programaticamente.</li></ul><p>Com essa estrutura em mente, vamos nos aprofundar em cada ferramenta em detalhes.</p><h4>Ferramenta de Busca de Documentos</h4><p>Esta ferramenta realiza uma <a href="https://www.elastic.co/docs/solutions/search/full-text">busca de texto completo</a> no índice do Elasticsearch para recuperar os documentos mais relevantes com base na consulta do usuário. Ele destaca correspondências-chave e oferece uma visão geral rápida com pontuações de relevância.</p><pre><code>server.registerTool(
  "search_docs",
  {
    title: "Search Documents",
    description:
      "Search for documents in Elasticsearch using full-text search. Returns the most relevant documents with their content, title, tags, and relevance score.",
    inputSchema: {
      query: z
        .string()
        .describe("The search query terms to find relevant documents"),
      max_results: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of results to return"),
    },
    outputSchema: {
      results: z.array(SearchResultSchema),
      total: z.number(),
    },
  },
  async ({ query, max_results }) => {
    if (!query) {
      return {
        content: [
          {
            type: "text",
            text: "Query parameter is required",
          },
        ],
        isError: true,
      };
    }

    try {
      const response = await _client.search({
        index: INDEX,
        size: max_results,
        query: {
          bool: {
            must: [
              {
                multi_match: {
                  query: query,
                  fields: ["title^2", "content", "tags"],
                  fuzziness: "AUTO",
                },
              },
            ],
            should: [
              {
                match_phrase: {
                  title: {
                    query: query,
                    boost: 2,
                  },
                },
              },
            ],
          },
        },
        highlight: {
          fields: {
            title: {},
            content: {},
          },
        },
      });

      const results: SearchResult[] = response.hits.hits.map((hit: any) => {
        const source = hit._source as Document;

        return {
          id: source.id,
          title: source.title,
          content: source.content,
          tags: source.tags,
          score: hit._score ?? 0,
        };
      });

      const contentText = results
        .map(
          (r, i) =>
            `[${i + 1}] ${r.title} (score: ${r.score.toFixed(
              2,
            )})\n${r.content.substring(0, 200)}...`,
        )
        .join("\n\n");

      const totalHits =
        typeof response.hits.total === "number"
          ? response.hits.total
          : (response.hits.total?.value ?? 0);

      return {
        content: [
          {
            type: "text",
            text: `Found ${results.length} relevant documents:\n\n${contentText}`,
          },
        ],
        structuredContent: {
          results: results,
          total: totalHits,
        },
      };
    } catch (error: any) {
      console.log("Error during search:", error);

      return {
        content: [
          {
            type: "text",
            text: `Error searching documents: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);</code></pre><p><em>Configuramos <code><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-fuzzy-query">fuzziness</a>: “AUTO”</code> para ter uma tolerância variável de erros de digitação com base no comprimento do token que está sendo analisado. Também definimos <code>title^2</code> para aumentar a pontuação dos documentos onde a correspondência ocorre no campo de título.</em></p><h4>Ferramenta summarize_and_cite</h4><p>Esta ferramenta gera um resumo baseado em documentos recuperados na busca anterior. Usa o modelo <code>gpt-4o-mini</code> da OpenAI para sintetizar as informações mais relevantes e responder à pergunta do usuário, fornecendo respostas derivadas diretamente dos resultados da busca. Além do resumo, também retorna metadados de citação para os documentos de origem usados.</p><pre><code>server.registerTool(
  "summarize_and_cite",
  {
    title: "Summarize and Cite",
    description:
      "Summarize the provided search results to answer a question and return citation metadata for the sources used.",
    inputSchema: {
      results: z
        .array(SearchResultSchema)
        .describe("Array of search results from search_docs"),
      question: z.string().describe("The question to answer"),
      max_length: z
        .number()
        .optional()
        .default(500)
        .describe("Maximum length of the summary in characters"),
      max_docs: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of documents to include in the context"),
    },
    outputSchema: {
      summary: z.string(),
      sources_used: z.number(),
      citations: z.array(
        z.object({
          id: z.number(),
          title: z.string(),
          tags: z.array(z.string()),
          relevance_score: z.number(),
        })
      ),
    },
  },
  async ({ results, question, max_length, max_docs }) => {
    if (!results || results.length === 0 || !question) {
      return {
        content: [
          {
            type: "text",
            text: "Both results and question parameters are required, and results must not be empty",
          },
        ],
        isError: true,
      };
    }

    try {
      const used = results.slice(0, max_docs);

      const context = used
        .map(
          (r: SearchResult, i: number) =>
            `[Document ${i + 1}: ${r.title}]\\n${r.content}`
        )
        .join("\n\n---\n\n");

      // Generate summary with OpenAI
      const completion = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content:
              "You are a helpful assistant that answers questions based on provided documents. Synthesize information from the documents to answer the user's question accurately and concisely. If the documents don't contain relevant information, say so.",
          },
          {
            role: "user",
            content: `Question: ${question}\\n\\nRelevant Documents:\\n${context}`,
          },
        ],
        max_tokens: Math.min(Math.ceil(max_length / 4), 1000),
        temperature: 0.3,
      });

      const summaryText =
        completion.choices[0]?.message?.content ?? "No summary generated.";

      const citations = used.map((r: SearchResult) => ({
        id: r.id,
        title: r.title,
        tags: r.tags,
        relevance_score: r.score,
      }));

      const citationText = citations
        .map(
          (c: any, i: number) =>
            `[${i + 1}] ID: ${c.id}, Title: "${c.title}", Tags: ${c.tags.join(
              ", ",
            )}, Score: ${c.relevance_score.toFixed(2)}`,
        )
        .join("\n");

      const combinedText = `Summary:\\n\\n${summaryText}\\n\\nSources used (${citations.length}):\\n\\n${citationText}`;

      return {
        content: [
          {
            type: "text",
            text: combinedText,
          },
        ],
        structuredContent: {
          summary: summaryText,
          sources_used: citations.length,
          citations: citations,
        },
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error generating summary and citations: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);</code></pre><p>Por fim, precisamos iniciar o servidor usando <a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#stdio">stdio</a>. Isso significa que o cliente MCP se comunicará com nosso servidor lendo e escrevendo nos fluxos padrão de entrada e saída. Stdio é a opção de transporte mais simples e funciona bem para servidores MCP locais lançados como subprocessos pelo cliente. Adicione o seguinte código ao final do arquivo:</p><pre><code>const transport = new StdioServerTransport();
server.connect(transport);</code></pre><p>Agora compile o projeto usando o seguinte comando:</p><pre><code>npx tsc index.ts --target ES2022 --module node16 --moduleResolution node16 --outDir ./dist --strict --esModuleInterop</code></pre><p>Isso criará uma pasta <code>dist</code> e, dentro dela, um arquivo <code>index.js</code>.</p><h3>Carregue o servidor MCP no Claude Desktop</h3><p>Siga <a href="https://modelcontextprotocol.io/docs/develop/connect-local-servers">este guia</a> para configurar o servidor MCP com o Claude Desktop. No arquivo de configuração Claude, precisamos definir os seguintes valores:</p><pre><code>{
  "mcpServers": {
    "elasticsearch-rag-mcp": {
      "command": "node",
      "args": [   "/Users/user-name/app-dir/dist/index.js"
      ],
      "env": {
        "ELASTICSEARCH_ENDPOINT": "your-endpoint-here",
        "ELASTICSEARCH_API_KEY": "your-api-key-here",
        "OPENAI_API_KEY": "your-openai-key-here"
      }
    }
  }
}</code></pre><p>O valor <code>args</code> deve apontar para o arquivo compilado na pasta <code>dist</code>. Você também precisa definir as variáveis de ambiente no arquivo de configuração com exatamente os mesmos nomes definidos no código.</p><h3>Faça o teste</h3><p>Antes de executar cada ferramenta, clique em <strong>Busca e Ferramentas</strong> para garantir que as ferramentas estejam ativadas. Aqui você também pode ativar ou desativar cada uma delas:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/172981c2a54adabc70d5819013c3007670935605-1999x1002.png" style="width: 100%;" /><p>Por fim, vamos testar o servidor MCP no chat do Claude Desktop e começar a fazer perguntas:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03654c0f8c53c714f801fba8b25747071179209b-1999x1353.png" style="width: 100%;" /><p>Para a pergunta “<strong>Buscar documentos sobre métodos de autenticação e controle de acesso por função</strong>”, a ferramenta <code>search_docs</code> é executada e retorna os seguintes resultados:</p><pre><code>Most Relevant Documents:
Access Control and Role Management (highest relevance) - This document covers role-based access control (RBAC) principles, including ensuring users only have necessary permissions, regular auditing of user roles, revoking inactive accounts, and implementing just-in-time access for sensitive operations.
User Authentication with OAuth 2.0 - This document explains OAuth 2.0 authentication, which enables secure delegated access without credential sharing. It covers configuring identity providers, token management with limited scope and lifetime, and secure storage of refresh tokens.
Container Security Guidelines - While primarily about container security, this document touches on access control aspects like running containers as non-root users and avoiding embedded credentials.
Incident Response Playbook - This mentions role assignment during incidents (incident commander, communications lead, etc.), which relates to access control in emergency scenarios.
Logging Standards for Microservices - This document includes guidance on avoiding logging sensitive information, which is relevant to authentication security.</code></pre><p>A resposta é: &quot;Ótimo! Encontrei 5 documentos relevantes sobre métodos de autenticação e controle de acesso por função. Eis o que foi encontrado:&quot;</p><p>A chamada de ferramenta retorna os documentos fonte como parte da carga útil de resposta, que são posteriormente usados para gerar citações.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25ff4c7e9563d99752f95540dafdc7fd211a66e3-800x530.gif" style="width: 100%;" /><p>Também é possível encadear várias ferramentas em uma única interação. Neste caso, o Claude Desktop analisa a pergunta do usuário e determina que precisa primeiro chamar <code>search_docs</code> para recuperar documentos relevantes e depois passar esses resultados para <code>summarize_and_cite</code> para gerar a resposta final, tudo isso sem exigir prompts separados do usuário:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6a8a46acb4236e77058f18bcd2f0737b5882c05-1999x1101.png" style="width: 100%;" /><p>Neste caso, para a consulta “<strong>Quais são as principais recomendações para melhorar a autenticação e o controle de acesso em nossos sistemas? Inclua referências.</strong>”, obtivemos os seguintes resultados:</p><pre><code>Based on the documentation, here are the main recommendations to improve authentication and access control across your systems:

Key Recommendations

1. Implement Role-Based Access Control (RBAC)
Ensure users have only the permissions necessary for their job functions. This principle of least privilege minimizes security risks by limiting access to what's truly needed. [1]

2. Regular Access Audits
Conduct regular audits of user roles and promptly revoke access for inactive accounts. This helps prevent unauthorized access from dormant or compromised accounts. [1]

3. Just-in-Time (JIT) Access
Implement just-in-time access for sensitive operations, where permissions are granted only when needed and for a limited duration. This reduces the attack surface by minimizing standing privileges. [1]

4. OAuth 2.0 for Secure Authentication
Use OAuth 2.0 to enable secure delegated access without sharing user credentials. This protocol allows third-party applications to access resources on behalf of users without exposing passwords. [2]

5. Token Security and Management
Configure your identity provider to issue tokens with limited scope and lifetime

Store refresh tokens securely
Validate access tokens consistently to maintain security [2]
References

Access Control and Role Management (Tags: security, access-control)
User Authentication with OAuth 2.0 (Tags: authentication, oauth)
These recommendations work together to create a defense-in-depth approach, where multiple security layers protect your systems from unauthorized access.</code></pre><p>Como na etapa anterior, podemos ver a resposta de cada ferramenta para esta pergunta:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cb606d356b2f7d5e4878a5eff71bc881869ac0ee-800x585.gif" style="width: 100%;" /><p><em>Nota: Se aparecer um submenu perguntando se você aprova o uso de cada ferramenta, selecione <strong>Permitir sempre</strong> ou <strong>Permitir uma vez</strong>.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aea942ba9b0037526ea215bec65690f1a5c3099c-1522x250.png" style="width: 100%;" /><h2>Conclusão</h2><p>Os servidores MCP representam um passo significativo rumo à padronização das ferramentas LLM para aplicações locais e remotas. Embora a compatibilidade total ainda esteja em andamento, estamos avançando nessa direção.</p><p>Neste artigo, aprendemos como criar um servidor MCP personalizado em TypeScript que conecta o Elasticsearch a aplicações baseadas em LLM. Nosso servidor expõe duas ferramentas: <code>search_docs</code> para recuperar documentos relevantes usando Query DSL; e <code>summarize_and_cite</code> para gerar resumos com citações via modelos OpenAI e Claude Desktop como UI.</p><p>O futuro da compatibilidade entre diferentes provedores de clientes e servidores parece promissor. As próximas etapas incluem adicionar mais funcionalidades e flexibilidade ao seu agente. Existe um <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">artigo</a> prático sobre como parametrizar suas consultas usando modelos de pesquisa para ter precisão e flexibilidade.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apresentando permissões de somente leitura para dashboards do Kibana]]></title>
            <description><![CDATA[Apresentamos dashboards de somente leitura no Kibana, oferecendo aos criadores de dashboards controles detalhados de compartilhamento para manter os resultados precisos e protegidos contra alterações indesejadas.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/kibana-dashboards-read-only-permissions</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/kibana-dashboards-read-only-permissions</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Fabio Busatto, Teresa Alvarez Soler]]></dc:creator>
            <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/11e578bc317aea30d2e10ccc0334a532f6af2ef9-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Você já passou por isso. Você passou uma hora criando o dashboard perfeito para monitorar seus logs: cada gráfico, cada filtro e cada rótulo. Você o compartilha com sua equipe. Alguns dias depois, você o abre e percebe que algo está errado: um colega ajustou uma consulta ou alguém alterou o intervalo de datas. Talvez essa pessoa achasse que estava ajudando. Agora você está vasculhando revisões e questionando cada número. Soa familiar?</p><p>É exatamente por isso que construímos <strong>dashboards de somente leitura</strong>. É o controle que você vinha pedindo. Compartilhe dashboards com confiança, sem se preocupar que a próxima pessoa com acesso de edição os altere ou danifique.</p><p>Observação: permissões de somente leitura estão disponíveis no Elastic Cloud Serverless e a partir da versão 9.3 para o Elastic Cloud Hosted e o Elastic Self-Managed.</p><h2>Quando “todo mundo pode editar” atrapalha</h2><p>No Kibana, <em>compartilhamento </em>geralmente significava permissões no nível do espaço. Se alguém pode criar dashboards em um espaço, também pode editar ou excluir os de qualquer outra pessoa. Isso é ótimo para colaboração até deixar de funcionar. Uma edição acidental pode levar a decisões erradas, perda de confiança e muito retrabalho.</p><p>Já ouvimos as soluções alternativas: <strong>&quot;Colocamos &#x27;somente leitura&#x27; no nome do dashboard e esperamos que as pessoas percebam.&quot;</strong> Ou: <strong>&quot;Nós os marcamos e cruzamos os dedos.&quot;</strong> Esperança não é um modelo de permissão. Você precisava de uma forma real de bloquear o dashboard sem bloquear todo mundo do espaço.</p><h2>O que realmente dá errado</h2><p>Deb e Kevin têm acesso de edição ao dashboard de monitoramento de logs dentro do espaço Operações. Kevin faz algumas mudanças nos gráficos. Quando Deb volta, os números não correspondem ao que ela apresentou. Ela precisa rastrear o que mudou (muitas vezes de memória), consertar e se perguntar quantos relatórios foram enviados com dados ruins.</p><h2>Dashboards de somente leitura: Propriedade e controle que fazem sentido</h2><p>Dashboards de somente leitura corrigem isso ao dar a você controle para decidir se outros usuários podem editar o dashboard. Quando você compartilha um dashboard, escolhe: <strong>editar</strong> (padrão, igual a hoje) ou <strong>visualizar</strong>. No modo de <strong>visualização </strong>, somente você (e os administradores do Kibana) podem mudar ou excluir isso. Todos os outros usuários podem abrir, usar e confiar, mas não podem modificar.</p><h3>O que você recebe</h3><ul><li><strong>Integridade do dashboard:</strong> no modo de <strong>visualização</strong>, outros usuários com acesso de edição no espaço não podem modificar ou excluir o dashboard. Se tentarem, são informados de que está bloqueado. Seus gráficos e sua lógica permanecem como você os deixou.</li><li><strong>Você mantém o controle:</strong> Você é o dono. Você sempre pode editar, refinar e atualizar. Compartilhar como somente visualização não bloqueia o acesso; ele fixa a versão que todos os outros veem.</li><li><strong>Ciclo de vida flexível:</strong> você pode voltar a colocar um dashboard em “Pode editar” a qualquer momento. E os administradores do Kibana ainda podem gerenciar todos os dashboards (por exemplo, se o proprietário sair). Sem impasses.</li></ul><p>Você pode compartilhar amplamente dashboards finalizados e de alta importância estratégica e saber que eles permanecerão consistentes. Está disponível em <strong>todos os níveis e ofertas da Elastic</strong>, incluindo Serverless.</p><h3>Quem pode fazer o quê?</h3><p>Referência rápida por função:</p><ul><li><strong>Proprietário do dashboard:</strong> você o criou; tem acesso total de edição.</li><li><strong>Administrador do Kibana:</strong> pode gerenciar todos os dashboards.</li><li><strong>Usuário com permissão de edição no espaço:</strong> pode criar e editar seus dashboards; não pode editar ou excluir dashboards de somente leitura.</li><li><strong>Usuário com permissão de visualização no espaço:</strong> só pode visualizar (e listar) dashboards.</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ação</td><td style="border: 1px solid #ccc; padding: 4px;">Proprietário do dashboard</td><td style="border: 1px solid #ccc; padding: 4px;">Administrador do Kibana</td><td style="border: 1px solid #ccc; padding: 4px;">Usuário com permissão para editar no espaço</td><td style="border: 1px solid #ccc; padding: 4px;">Usuário com permissão de visualização</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Listar e visualizar dashboards</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Criar novos dashboards</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Modificar/excluir dashboards editáveis</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Modificar/excluir dashboards de somente leitura</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✔</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td><td style="border: 1px solid #ccc; padding: 4px;">✘</td>
                    </tr>
              </tbody>
            </table>
          <h2>Como ativar o modo somente leitura</h2><p>Você pode configurar o modo somente leitura ao salvar um novo dashboard ou mais tarde no menu de compartilhamento.</p><h3>Ao salvar um novo dashboard</h3><ul><li>Crie seu dashboard e clique em <strong>Salvar</strong>.</li><li>Na janela &quot;Salvar como novo dashboard&quot;, encontre <strong>Permissões</strong>.</li><li>Alterar de <strong>Pode editar</strong> para <strong>Pode visualizar</strong>.</li><li>Clique em <strong>Salvar</strong>. Concluído. É somente leitura para todos os outros.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42a71d1bb55f9d50bd079f53bf45a0e1999b27f7-1214x1306.png" style="width: 100%;" /><h2>Para um dashboard que você já possui</h2><ul><li>Abra o dashboard.</li><li>Abra o menu <strong>Compartilhar dashboard</strong>.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e8405938557c879b1d4c262b98cf5a7f66408c04-1246x264.png" style="width: 100%;" /><ul><li>Na janela de compartilhamento, localize <strong>Permissões</strong> e alterne para <strong>Pode visualizar</strong>. A mudança se aplica imediatamente; outros usuários no espaço não podem mais editar ou excluir o dashboard.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/849405bc32701f3ebe0def012d8ae3cf3813ea0a-996x750.png" style="width: 100%;" /><ul><li>Você pode passar o mouse sobre a ação <strong>Compartilhar</strong> para ver que tipo de permissões um determinado dashboard tem.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/80541ddb1b1bc567b0aeff693944ea8b6871d6a7-1270x320.png" style="width: 100%;" /><h3>Ver quais dashboards estão bloqueados</h3><p>Na lista principal Dashboards, os dashboards que você não pode editar ou excluir têm uma caixa de seleção desativada. Isso oferece uma maneira fácil de identificar o que é somente leitura.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24b2eba08df86174db949c662e7886c5aea1b460-1999x876.png" style="width: 100%;" /><p>No dashboard, você também verá que a ação Editar está desativada e uma dica de ferramenta será exibida, explicando que o dashboard foi definido como somente leitura.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e0a2f9da6dc854e876fc6dc2a7c3ef8b313b52ef-1358x330.png" style="width: 100%;" /><h2>Experimente</h2><p>Dashboards de somente leitura já estão disponíveis. Crie um dashboard, defina como <strong>Pode visualizar</strong> e compartilhe. Sua equipe recebe uma única fonte confiável, e você fica tranquilo. Não há mais “por favor, não edite” no título.</p><p>Queremos saber como você usa dashboards de somente leitura. Compartilhe seu feedback em nosso <a href="https://discuss.elastic.co">fórum comunitário</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A ferramenta shell não é uma solução milagrosa para engenharia de contexto]]></title>
            <description><![CDATA[Saiba quais ferramentas de recuperação de contexto existem para a engenharia de contexto, como elas funcionam e as vantagens e desvantagens.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/search-tools-context-engineering</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/search-tools-context-engineering</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Leonie Monigatti]]></dc:creator>
            <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f91e4d07915ba7bf3b7abf15fac8fab3350f7df2-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>As ferramentas mais importantes para o agente são as ferramentas de busca que ele pode usar para construir o próprio contexto. Postagens recentes do <a href="https://www.llamaindex.ai/blog/files-are-all-you-need">LlamaIndex</a> e <a href="https://x.com/hwchase17/status/2011814697889316930">LangChain</a> suscitaram uma discussão: <em>uma ferramenta de linha de comando e um sistema de arquivos são tudo o que o agente precisa para engenharia de contexto? </em>Infelizmente, a discussão rapidamente se desviou para o foco errado: sistema de arquivos x banco de dados.</p><p>Esta postagem volta a se concentrar na questão: <em>quais são as interfaces de busca que um agente precisa para construir o próprio contexto?</em> Primeiro, ele aborda as vantagens entre ferramentas de linha de comando e ferramentas dedicadas de banco de dados. A partir daí, oferece um framework prático para encontrar as interfaces certas para as necessidades do seu agente.</p><h2>O que &quot;construir contexto&quot; realmente significa para um agente?</h2><p>Nos primeiros <a href="https://www.elastic.co/what-is/retrieval-augmented-generation">pipelines de Retrieval-Augmented Generation (RAG)</a>, o desenvolvedor projetou um pipeline de recuperação fixo, e o modelo de linguagem grande (LLM) era um receptor passivo do contexto. Essa limitação era fundamental: o contexto era recuperado em cada consulta, fosse necessário ou não, sem checar se realmente ajudava.</p><p>Com a transição para o RAG agêntico, os agentes agora têm acesso a um conjunto de ferramentas de busca para criar o próprio contexto. Por exemplo, tanto o Claude Code [1] quanto o Cursor [2] permitem que o agente escolha diferentes ferramentas de busca e até as combine para consultas encadeadas, dependendo do que a tarefa realmente exige.</p><h2>Quais interfaces de busca existem para engenharia de contexto?</h2><p>O contexto pode estar em diferentes locais, como na web, em um sistema de arquivos local ou em um banco de dados. O agente pode interagir com cada uma dessas fontes de dados fora de contexto em diferentes ferramentas:</p><ul><li><strong>As ferramentas de linha de comando</strong> podem executar comandos de linha de comando e ter acesso ao sistema de arquivos local. Alguns exemplos de ferramentas de linha de comando integradas são <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool">a ferramenta bash da API Claude</a>, <a href="https://docs.openclaw.ai/tools/exec">a ferramenta exec do OpenClaw</a> e <a href="https://docs.langchain.com/oss/python/integrations/tools/bash">a ferramenta de linha de comando do LangChain</a>.</li><li><strong>Ferramentas de banco de dados dedicadas,</strong> como ferramentas de um servidor Model Context Protocol (MCP) (por exemplo, o <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">servidor MCP do Elastic Agent Builder)</a> ou ferramentas personalizadas (por exemplo, <code>run_esql(query)</code> ou <code>db_list_index()</code>), podem consultar bancos de dados.</li><li><strong>Ferramentas dedicadas de busca de arquivos</strong> podem buscar e ler arquivos locais (ou carregados), sem acesso total à linha de comando. Alguns exemplos de ferramentas integradas de busca de arquivos são o <a href="https://ai.google.dev/gemini-api/docs/file-search">File Search Tool da API do Gemini</a> e o <a href="https://developers.openai.com/api/docs/guides/tools-file-search">File Search Tool da OpenAI</a>.</li><li><strong>Ferramentas de busca na web</strong> podem recuperar informações da web.</li><li><strong>Ferramentas de memória</strong> armazenam e recuperam da memória de longo prazo (independentemente de como esteja armazenada).</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/115f20c8ded259e508f51524b2c06bdc702d70ab-1999x1050.png" style="width: 100%;" /><p>Como você pode ver, a ferramenta shell é versátil e pode ser usada para recuperar contexto de diferentes fontes de dados, incluindo:</p><ul><li><strong>Sistema de arquivos:</strong> o agente explora a estrutura de diretórios (ls, find), busca conteúdo relevante (grep, cat) e repete o processo até obter contexto suficiente.</li><li><strong>Banco de dados:</strong> o agente pode usar ferramentas de linha de comando (CLI) para banco de dados (p. ex., <a href="https://www.elastic.co/docs/reference/query-languages/sql/sql-cli"><code>elasticsearch-sql-cli</code></a>), chamar APIs HTTP via curl ou executar scripts. Isso é útil principalmente em combinação com as habilidades do agente, que são exemplos reutilizáveis e documentados inseridos no contexto do agente para orientar o uso correto das ferramentas (p. ex., <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills para Elasticsearch</a>).</li><li><strong>Web: </strong>o agente pode executar buscas na web usando o comando curl via API de um provedor de busca.</li></ul><p>No entanto, a ferramenta de linha de comando dá acesso direto ao sistema e, portanto, exige medidas de segurança, como execução em ambiente sandbox isolado e logging de todos os comandos executados.</p><h2>Quando usar as diferentes interfaces de busca</h2><p>A interface de busca certa depende dos seus dados, dos seus padrões de consulta e do seu caso de uso. Esta seção serve como um ponto de partida prático.</p><h3>Sistemas de arquivos não tornam bancos de dados obsoletos</h3><p>A discussão entre sistemas de arquivos e bancos de dados não envolve a camada de armazenamento. Por exemplo, o LangChain explica que <a href="https://x.com/hwchase17/status/2011814697889316930">seu sistema de memória</a> na verdade não armazena memória em um sistema de arquivos real. Em vez disso, ele armazena a memória em um banco de dados e a <em>representa</em> como um conjunto de arquivos para o agente [3].</p><p>Sistemas de arquivos são uma escolha natural para casos de uso nativos de arquivos, como agentes de codificação. Eles também funcionam bem como bloco de notas temporário ou memória de trabalho e para casos de usuário único ou agente único em que a concorrência não é preocupação. Nesses casos, um sistema de arquivos físico ou a representação dos dados como sistema de arquivos dá flexibilidade antes de se comprometer com uma interface específica.</p><p>Porém, o armazenamento no sistema de arquivos tem desvantagens reais, como concorrência fraca, aplicação manual de esquemas e transações atômicas. Esses fatores ficam mais evidentes quando sua aplicação precisa redimensionar ou migrar para um panorama multi-agente. Qualquer pessoa que ignore essas desvantagens está condenada a <a href="https://dx.tips/oops-database">reinventar penosamente bancos de dados piores</a>, sem as décadas de engenharia por trás da segurança das transações ou do controle de acesso que os bancos de dados de produção já oferecem. Além disso, na maioria dos contextos de negócios, você não escolhe usar o banco de dados, já que ele já existe, armazenando dados críticos para a empresa.</p><h3>Ferramenta de linha de comando e sistema de arquivos</h3><p>Ferramenta de linha de comando é o ponto de partida natural para buscas no sistema de arquivos. Atualmente, os agentes de codificação estão gerando um grande avanço no campo. Como eles trabalham com código em arquivos locais, são naturalmente casos de uso que envolvem muitos arquivos. Portanto, os LLMs são ajustados na fase pós-treinamento nas tarefas de codificação. É por isso que muitos LLMs são bons não só em escrever código como em usar comandos de linha de comando e navegar por sistemas de arquivos.</p><p>Usar uma ferramenta de linha de comando com CLIs integradas, como <code>ls</code> e <code>grep</code>, para encontrar arquivos é eficaz. Com grep, uma consulta como &quot;Encontre todos os arquivos que importam <code>matplotlib</code>&quot; é rápida, precisa e barata. Mas, quando o agente precisa lidar com consultas conceituais, como &quot;Como nosso app lida com falhas na autenticação?&quot;, a correspondência de padrões com o grep pode atingir um teto muito rápido. Várias alternativas que trazem capacidades de busca semântica para a linha de comando surgiram para preencher essa lacuna, incluindo <a href="https://github.com/jina-ai/jina-grep-cli"><code>jina-grep</code></a>.</p><p>No entanto, grep e muitas das alternativas de busca semântica funcionam em O(n) sobre o corpus. Nos casos de uso em bases de código, isso pode ser suficiente. No entanto, se seus dados aumentarem, a latência será perceptível. Nesse caso, é necessário um datastore indexado para manter o desempenho.</p><h3>Ferramenta Shell + banco de dados</h3><p>Outra forma de incluir aos seus dados mais recursos de busca, como busca semântica ou híbrida, é armazená-los em um banco de dados, como o Cursor. Além disso, quando os dados exigem junções relacionais complexas ou agregações, é indispensável uma interface de banco de dados.</p><p>Quando os dados estão em um banco de dados em vez de no sistema de arquivos, uma ferramenta de linha de comando pode funcionar como uma interface leve para banco de dados em determinados casos de uso. Se suas consultas forem simples o suficiente para uma CLI ou uma chamada de curl, uma ferramenta dedicada de banco de dados pode adicionar complexidade desnecessária.</p><p>Essa abordagem também é adequada nas fases iniciais de exploração, quando você ainda não sabe quais padrões de consulta seu agente realmente desenvolverá. Nesse caso, as habilidades do agente podem fornecer ao agente estrutura suficiente para realizar consultas corretamente, sem precisar se comprometer com uma ferramenta desenvolvida especificamente para isso. No entanto, quando o agente precisa de várias iterações para encontrar a maneira correta de consultar o banco de dados para tarefas repetitivas, a sobrecarga de tokens ao usar uma ferramenta de linha de comando como interface deixa de justificar a simplicidade de evitar mais uma ferramenta.</p><h3>Ferramenta dedicada de banco de dados</h3><p>Principalmente quando padrões de consulta repetidos são estruturados ou analíticos, ferramentas dedicadas a banco de dados são necessárias. Um <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">post do blog da Vercel e da Braintrust</a> comparou agentes com diferentes conjuntos de ferramentas de busca para tarefas reais de recuperação em dados semiestruturados, como chamados de suporte ao cliente e transcrições de chamadas de vendas (por exemplo: &quot;Quantos problemas abertos mencionam &#x27;segurança&#x27;?&quot; ou &quot;Encontre problemas onde alguém relatou um bug e depois alguém enviou um PR alegando corrigir.&quot;) [4].</p><p>Agentes com ferramentas dedicadas de banco de dados usavam menos tokens, eram mais rápidos e cometiam menos erros do que agentes com apenas uma ferramenta de linha de comando e um sistema de arquivos. A lição é que ferramentas diretas de banco de dados são a opção certa quando a consulta exige raciocínio analítico sobre dados semiestruturados.</p><h3>Combinando interfaces de busca</h3><p>Nenhuma interface de busca única lida bem com todas as consultas. Por exemplo, o Cursor combina ferramentas de linha de comando (para buscas via grep) e ferramentas de busca semântica e permite que o agente selecione a ferramenta certa com base nas instruções do usuário. Eles relatam que o agente escolhe o grep para buscar símbolos ou strings específicos, a busca semântica para perguntas conceituais ou comportamentais e ambos para tarefas exploratórias.</p><p>O experimento Vercel relata o mesmo: seu agente híbrido, com acesso tanto a uma ferramenta de linha de comando quanto a uma ferramenta de banco de dados dedicada, obteve o melhor desempenho entre todos os agentes testados, usando primeiro as ferramentas de banco de dados dedicadas e, em seguida, confirmando os resultados via busca no sistema de arquivos. No entanto, essa abordagem usa mais tokens e tempo para raciocinar sobre a escolha e validação da ferramenta.</p><p>Em ambos os exemplos, o padrão é o mesmo: a composição supera qualquer interface única, mas tem como contrapartida o aumento no custo e da latência.</p><h2>Recomendações práticas para encontrar o conjunto certo de ferramentas</h2><p>O conjunto certo de interfaces de busca é pequeno, específico e adequado aos padrões reais de consulta do seu agente. A prática recomendada atual é ter um agente com o mínimo de ferramentas possível, em vez de ter um agente com centenas de ferramentas MCP. Isso ocorre porque a desvantagem de expor todas as ferramentas possíveis de antemão enche a janela de contexto e confunde o agente sobre qual ferramenta realmente usar. Por exemplo, o Claude Code supostamente tem apenas cerca de 20 ferramentas.</p><p>Em vez disso, a ideia da divulgação progressiva é começar com um conjunto mínimo de ferramentas e deixar o agente descobrir outros recursos somente quando necessário. Pesquisas da Anthropic [5] e da Cursor [6] mostraram que essa abordagem gera uma economia de tokens entre 47%–85%. O Claude Code, por exemplo, implementa isso diretamente, permitindo que o agente descubra de forma incremental como consultar uma API ou um banco de dados, sem que esse conhecimento consuma contexto em cada chamada LLM.</p><p>Após saber os padrões de consulta do agente, você pode revisitar o conjunto de ferramentas de busca às quais o agente tem acesso como padrão. Uma forma útil de pensar sobre essa troca é o <a href="https://www.elastic.co/search-labs/blog/database-retrieval-tools-context-engineering#building-the-right-database-retrieval-tools-%5C(%E2%80%9Clow-floor,-high-ceiling%E2%80%9D%5C">princípio &quot;piso baixo, teto alto&quot;</a> para decidir quais ferramentas devem ser selecionadas. Ferramentas &quot;teto alto&quot; não limitam o potencial do agente. Por exemplo, uma ferramenta de linha de comando versátil permite que o agente escreva consultas completas ao banco de dados, inclusive as ambíguas, mas ao custo de sobrecarga no raciocínio, maior latência e menor confiabilidade.</p><p>Ferramentas de baixo piso são o oposto. São ferramentas especializadas que abrangem consultas específicas e são imediatamente acessíveis ao agente com o mínimo de sobrecarga de raciocínio, gerando menor custo e maior confiabilidade. Mas eles precisam de engenharia inicial, não conseguem cobrir todas as possíveis consultas e podem dificultar para o agente escolher a ferramenta certa.</p><p>Pense em cada ferramenta como parte de um espectro: ferramentas com baixo limiar de uso são fáceis para o agente usar corretamente, mas têm escopo limitado. Já as ferramentas com alto teto (maior potencial) são versáteis, mas exigem mais raciocínio para serem usadas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6d1b973be4b0a0a25c99c74f02a47e98395a3f7-1200x630.png" style="width: 100%;" /><p>A maioria dos agentes precisa de uma combinação de diferentes ferramentas de busca. Porém, cada ferramenta precisa justificar sua inclusão. Recomendamos começar com uma ferramenta de busca versátil (por exemplo, uma ferramenta <code>search_database()</code> ou uma ferramenta de linha de comando). Em seguida, reutilize os registros de comando que você já mantém para fins de segurança, a fim de monitorar o que seu agente realmente faz, incluindo chamadas de ferramentas, tentativas repetidas e número de chamadas por consulta de usuário. E, quando você notar um padrão de consulta se repetindo ou falhando, é hora de criar uma ferramenta específica para ele.</p><h2>Resumo</h2><p>O debate entre sistema de arquivos e banco de dados está desviando a atenção da verdadeira questão que os engenheiros deveriam se fazer: <em>quais são as interfaces de busca adequadas que o agente precisa para construir o próprio contexto?</em> A resposta mais provável é: <em>nenhuma</em>.</p><p>A ferramenta de linha de comando é versátil para interagir com diferentes fontes fora do contexto e, portanto, é um bom ponto de partida. Por outro lado, é menos eficiente e preciso nos casos de uso com consultas analíticas estruturadas do que ferramentas dedicadas a bancos de dados.</p><p>O objetivo é encontrar o conjunto mínimo de ferramentas de busca que lide bem com os padrões de consulta reais do seu agente. Comece com uma ferramenta de linha de comando e registre em log o que seu agente realmente faz. Quando um padrão de consulta se repetir e falhar, é hora de projetar ferramentas especializadas.</p><h2>Referências</h2><p>1. Thariq (Anthropic). <a href="https://x.com/trq212/status/2027463795355095314">Lessons from Building Claude Code: Seeing like an Agent</a> (2026).</p><p>2. Cursor: Documentation. <a href="https://cursor.com/docs/agent/tools/search">Semantic &amp; agentic search</a> (2026).</p><p>3. Harrison Chase (LangChain). <a href="https://x.com/hwchase17/status/2011814697889316930">How we built Agent Builder’s memory system</a> (2026).</p><p>4. Ankur Goyal (Braintrust) and Andrew Qu (Vercel). <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">Testing if &quot;bash is all you need&quot;</a> (2026).</p><p>5. Anthropic. <a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a> (2025).</p><p>6. Cursor. <a href="https://cursor.com/blog/dynamic-context-discovery">Dynamic context discovery</a> (2026).</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Réplicas do Elasticsearch para balanceamento de carga em Serverless]]></title>
            <description><![CDATA[Aprenda como o Elastic Cloud Serverless ajusta automaticamente as réplicas de índice com base na carga de busca, garantindo um desempenho ideal de consulta sem configuração manual.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-replicas-load-balancing-serverless</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-replicas-load-balancing-serverless</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Andrei Dan]]></dc:creator>
            <pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3c4c1e99b892f026b7aba098973593f8298e2ea6-1280x717.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>No Elastic Cloud Serverless, ajustamos automaticamente o número de réplicas para seus índices com base na carga de busca, garantindo um desempenho ótimo de consultas sem qualquer configuração manual. Neste blog, vamos explicar como as réplicas são escaladas, quando o sistema as adiciona ou remove, e o que isso significa para seus índices.</p><h2>A festa está ficando cheia</h2><p>Você está organizando uma festa da pizza. Você tem alguns amigos ajudando a servir, cada um em diferentes pontos da sala. Você dá uma pizza para cada amigo, e eles começam a distribuir fatias para os convidados famintos conforme chegam.</p><p>No começo, as coisas correm tranquilamente. Alguns convidados vão chegando, seus amigos servem as fatias, e todo mundo fica feliz. Mas então a notícia sobre suas pizzas de massa fermentada começa a se espalhar. A campainha continua tocando. Os convidados chegam em massa. Logo, uma multidão começa a se formar em torno de um de seus amigos, aquele que está segurando a pizza de pepperoni, que todo quer.</p><p>Seu amigo com a pizza de pepperoni está sobrecarregado. Os convidados estão esperando, ficando impacientes, e uma grande fila se formou. Enquanto isso, seu amigo segurando a pizza margherita está parado com quase ninguém pedindo uma fatia.</p><p>O que você faz?</p><p>Você pede mais algumas pizzas de pepperoni e as entrega para outros amigos. Agora, três amigos estão segurando fatias de pepperoni em vez de um. A multidão se espalha e, de repente, você pode servir três vezes mais convidados ao mesmo tempo.</p><p>Algumas coisas ficam claras conforme você organiza mais festas:</p><ul><li><strong>Nem todas as pizzas são igualmente populares.</strong> Algumas têm alta demanda, enquanto outras têm menos interessados. Você não precisa de &quot;cópias&quot; extras das impopulares; você precisa de mais das que têm filas.</li><li><strong>Peça mais pizzas antes que a fila fique muito longa.</strong> Se esperar até que seu amigo esteja completamente sobrecarregado e os convidados estejam indo embora com raiva, você já esperou demais. Melhor pegar uma pizza extra quando você vê uma multidão se formando.</li><li><strong>Não jogue as pizzas fora muito rápido.</strong> Só porque a multidão ao redor do pepperoni diminuiu por cinco minutos não significa que a correria acabou. Talvez eles estejam apenas reabastecendo as bebidas, ou até conversando entre si (isso ainda existe?). Mantenha as pizzas extras prontas. Se a pausa continuar por um tempo, você pode guardá-las.</li><li><strong>Você só pode distribuir tantas pizzas quanto o número de amigos que estão ajudando.</strong> Se você só tem quatro amigos ajudando, dez pizzas não mudarão o resultado. Apenas quatro podem ser servidas de uma vez. Ajuste a quantidade de pizzas ao número de mãos disponíveis.</li><li><strong>Quando um amigo for embora, pegue a pizza dele.</strong> Se algum dos seus amigos precisar sair, pegue a pizza dele imediatamente. Você não pode deixar pizzas sem vigilância. Entregue para outra pessoa ou guarde.</li></ul><h2>De pizzas a réplicas</h2><p>Vamos mapear isso de volta para o Elasticsearch.</p><p>Na nossa analogia, as pizzas são réplicas (cópias dos seus shards de índice), seus amigos ajudando a servir são nodes de busca, convidados famintos são consultas de busca e aquela pizza popular com uma multidão ao redor é um índice em alta com alta carga de busca.</p><p>Quando o tráfego de busca aumenta em um índice específico, criamos réplicas adicionais e as distribuir entre seus nodes de busca. Qualquer réplica pode atender a qualquer consulta para esse índice, assim como qualquer amigo com pepperoni pode distribuir fatias de pepperoni. Mais réplicas significam maior rendimento: três réplicas podem lidar com três vezes mais consultas por segundo de uma única réplica.</p><h2>Medindo a fome</h2><p>Antes de decidirmos quantas pizzas pedir, precisamos saber o quão faminta está a multidão.</p><p>O Elasticsearch rastreia a <strong>carga de busca</strong> para cada shard. É uma métrica que captura quanta atividade de busca um shard está gerenciando. Agregamos isso em todos os shards de um índice para entender a demanda total de busca.</p><p>O que mais importa é a <strong>carga relativa de busca</strong>: qual a proporção do tráfego total de busca do seu projeto está atingindo cada índice? Se um índice recebe 60% de todas as buscas enquanto outro recebe 5%, sabemos onde adicionar capacidade.</p><h2>A matemática por trás das pizzas</h2><p>Calculamos o número ótimo de réplicas seguindo esta fórmula:</p><pre><code>desired_replicas = min(ceil(L × N / (S × X)), N)</code></pre><p>Onde:</p><ul><li><strong>L</strong> = a carga de pesquisa relativa do índice (entre 0 e 1).</li><li><strong>N</strong> = o número de nodes de busca desejados em seu projeto.</li><li><strong>S</strong> = o número de fragmentos no índice.</li><li><strong>X</strong> = um limite para evitar pontos em alta (padrão: 0,5).</li></ul><p>Um exemplo: quatro nós de busca, um índice com dois shards principais recebendo 80% do tráfego de busca:</p><pre><code>desired_replicas = min(ceil(0.8 × 4 / (2 × 0.5)), 4)
                 = min(4, 4)
                 = 4</code></pre><p>Este índice em alta possui quatro réplicas distribuídas pelos nodes de busca.</p><p>O limiar X (padrão para 0,5) é importante. Não esperamos até que uma réplica seja completamente sobrecarregada; redimensionamos quando está com metade da capacidade. Distribua a pizza extra quando vir a multidão se formando, não quando os convidados já estiverem saindo.</p><h2>Aumente rápido, reduza devagar.</h2><p>Quando a carga de busca aumenta, adicionamos réplicas imediatamente. Não há motivo para deixar os usuários esperando.</p><p>Quando a carga de busca diminui, esperamos um pouco antes de agir. Precisamos ver uma demanda consistentemente baixa por cerca de 30 minutos antes de reduzir as réplicas. (Isso é para lidar com o tráfego irregular, onde um momento de silêncio não significa que a festa acabou.)</p><p>Isso é importante porque adicionar uma réplica tem um custo. A nova réplica copia os dados e esquenta seus caches antes de atender as consultas de forma eficiente. Remover réplicas de forma precipitada significa pagar constantemente esse custo inicial, já que o tráfego naturalmente flutua.</p><h2>Respeitando os limites da topologia</h2><p>As réplicas nunca podem exceder o número de nós de busca. Ter mais réplicas do que nós não traz benefício (você só pode servir tantas pizzas quanto amigos ajudam a servir fatias).</p><p>Quando nós são removidos do seu projeto, reduzimos as réplicas imediatamente para acompanhar. Sem esperar o cooldown, já que você não pode ter réplicas não atribuídas. No momento em que um amigo vai embora, removemos a pizza dele.</p><h2>O panorama maior do Serverless</h2><p>As réplicas para balanceamento de carga de busca funcionam junto com outros sistemas de autoescalonamento:</p><ul><li>O <strong>autoescalonamento de busca</strong> ajusta o número de nós de busca (quantos amigos estão ajudando).</li><li><strong>Réplicas para balanceamento de carga em buscas</strong> distribuem o tráfego ajustando a quantidade de réplicas por índice (quantas &quot;pizzas&quot; de cada tipo precisamos).</li><li><strong>O particionamento automático de fluxo de dados</strong> otimiza a quantidade de shards para gravações (como dividir cada pizza, explicado na <a href="https://www.elastic.co/search-labs/blog/datastream-autosharding-serverless">postagem anterior</a>).</li></ul><p>Um princípio importante de design: réplicas para balanceamento de carga não acionam diretamente o autoescalonamento da busca. Em vez disso, ao distribuir as requisições de busca entre mais réplicas, isso permite aumentar a utilização de recursos entre seus nós de busca. Essa maior utilização então ativa nossa lógica de autoescalonamento existente para aumentar a capacidade, se necessário. Réplicas para balanceamento de carga permitem que o autoescalonamento faça seu papel, garantindo que seus nós de busca estejam realmente sendo usados, em vez de ter todo o tráfego preso em uma única réplica enquanto outros nós ficam parados.</p><h2>O que isso significa para você</h2><p>Você não precisa prever quais índices serão populares. Você não precisa ajustar as réplicas manualmente quando os padrões de tráfego mudam. Você não precisa acordar às 3 da manhã porque um pico sobrecarregou seu índice mais ocupado.</p><p>O sistema observa onde as filas estão se formando e pede mais pizzas para esses lugares. Índices em baixa não desperdiçam recursos com réplicas desnecessárias. Os índices em alta recebem a capacidade necessária. Seu orçamento vai aonde realmente importa.</p><h2>Conclusão</h2><p>Na <a href="https://www.elastic.co/search-labs/blog/datastream-autosharding-serverless">postagem sobre particionamento automático</a>, garantimos que suas pizzas sejam cortadas corretamente. Agora, com réplicas para balanceamento de carga de busca, garantimos que você tenha pizzas suficientes, nas mãos certas, quando as multidões famintas chegarem.</p><p>Experimente <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> e deixe a logística da pizza com a gente.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Usando a API de Inferência Elasticsearch junto com modelos de Hugging Face]]></title>
            <description><![CDATA[Aprenda a conectar o Elasticsearch a modelos do Hugging Face usando endpoints de inferência e a construir um sistema multilíngue de recomendação de blogs com busca semântica e conclusões de chat.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/hugging-face-elasticsearch-inference-api</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/hugging-face-elasticsearch-inference-api</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1417d6ff033712206c9bd4bcc22074ee3437ce96-1999x1125.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Em atualizações recentes, o Elasticsearch introduziu uma integração nativa para conectar a modelos hospedados no <a href="https://endpoints.huggingface.co/">Hugging Face Inference Service</a>. Neste post, vamos explorar como configurar essa integração e realizar inferência por meio de chamadas simples de API usando um grande modelo de linguagem (LLM). Vamos usar <a href="https://huggingface.co/HuggingFaceTB/SmolLM3-3B">SmolLM3-3B</a>, um modelo leve de uso geral com bom equilíbrio entre uso de recursos e qualidade da resposta.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ddadf1976421a860a7d62087239adb9150d808b-1999x1388.png" style="width: 100%;" /><h2>Pré-requisitos</h2><ul><li><strong>Elasticsearch 9.3 ou Elastic Cloud Serverless: </strong>você pode criar uma implantação na nuvem seguindo <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">essas instruções</a>, ou você pode usar o <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart#local-dev-quick-start"><code>start-local</code></a> quickstart.</li><li><strong>Python 3.12: </strong>baixe o Python <a href="https://www.python.org/">aqui</a>.</li><li><strong>Hugging Face </strong><a href="https://huggingface.co/docs/hub/en/security-tokens">Token de acesso</a>.</li></ul><h2>Chat completions usando um endpoint de inferência do Hugging Face</h2><p>Primeiro, vamos construir um exemplo prático que conecta o Elasticsearch a um <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">endpoint de inferência</a> Hugging Face para gerar recomendações baseadas em IA a partir de uma coleção de artigos de blog. Para a base de conhecimento do app, usaremos um conjunto de dados de artigos de blog da empresa, que contém informações valiosas, mas frequentemente difíceis de navegar.</p><p>Com este endpoint, a <a href="https://www.elastic.co/docs/solutions/search/semantic-search">busca semântica</a> recupera os artigos mais relevantes para uma consulta específica, e um Hugging Face LLM gera recomendações curtas e contextuais com base nesses resultados.</p><p>Vamos dar uma olhada em uma visão geral do fluxo de informações que vamos criar:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1dfbc2323438feaaa42e13ab242dd1f7166f74aa-1200x676.png" style="width: 100%;" /><p>Neste artigo, testaremos a <strong>capacidade do SmolLM3-3B </strong>de<strong> </strong>combinar seu tamanho compacto com fortes capacidades de raciocínio multíngue e chamada de ferramentas. Com base em uma consulta de busca, enviaremos todo o conteúdo correspondente (em inglês e espanhol) para o LLM para gerar uma lista de artigos recomendados com uma descrição personalizada com base na consulta de busca e nos resultados.</p><p>Veja como poderia ser a UI de um site de artigos com um sistema de geração de recomendações por IA.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8d3b86b212f28ff279f2da67a33e6134039f0e4e-1999x949.png" style="width: 100%;" /><p>Você pode encontrar a implementação completa desta aplicação no <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/notebook.ipynb">notebook</a> vinculado.</p><h3>Configuração de endpoints de inferência do Elasticsearch</h3><p>Para usar o <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">endpoint de inferência do Hugging Face</a> no Elasticsearch, precisamos de dois elementos importantes: uma chave de API do Hugging Face e uma URL de endpoint do Hugging Face em execução. Ela deverá ficar assim:</p><pre><code>PUT _inference/chat_completions/hugging-face-smollm3-3b
{
    "service": "hugging_face",
    "service_settings": {
        "api_key": "hugging-face-access-token", 
        "url": "url-endpoint" 
    }
}</code></pre><p>O endpoint de inferência Hugging Face no Elasticsearch permite diferentes tipos de tarefas: <code>text_embedding</code>, <code>completion</code>, <code>chat_completion</code>, e <code>rerank</code>. Neste post do blog, usamos <code>chat_completion</code> porque precisamos que o modelo gere recomendações conversacionais baseadas nos resultados de busca e em um prompt do sistema. Esse endpoint nos permite realizar preenchimentos de chat diretamente do Elasticsearch de forma simples usando a API do Elasticsearch:</p><pre><code>POST _inference/chat_completion/hugging-face-smollm3-3b/_stream
{
  "messages": [
      { "role": "user", "content": "<user prompt>" }
  ]
}</code></pre><p>Isso servirá como o núcleo da aplicação, recebendo o prompt e os resultados de busca que passarão pelo modelo. Com a teoria explicada, vamos começar a implementar a aplicação.</p><h4>Configurando o endpoint de inferência no Hugging Face</h4><p>Para implantar o modelo Hugging Face, vamos usar <a href="https://huggingface.co/inference-endpoints/dedicated">implantações Hugging Face One-Click</a>, um serviço fácil e rápido para implantar endpoints de modelos. Lembre-se de que este é um serviço pago, e seu uso pode incorrer em custos adicionais. Esta etapa criará a instância do modelo que será usada para gerar as recomendações dos artigos.</p><p>Você pode escolher um modelo do catálogo de um clique.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b816e9fba1fe172687bf58f5143fb1f838c1077f-549x331.png" style="width: 100%;" /><p>Vamos escolher o modelo <strong>SmolLM3-3B</strong> :</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/610d3aba0429f3666c2df3616d513eb6a4397c0c-502x478.png" style="width: 100%;" /><p>A partir daqui, pegue o URL do endpoint do Hugging Face:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/025094ddb2cfbd1f0f216a5ec4e119b0f4fa2c42-646x328.png" style="width: 100%;" /><p>Como mencionado na <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">documentação de endpoints de inferência Hugging Face do Elasticsearch</a>, a geração de texto requer um modelo compatível com a API OpenAI. Por esse motivo, precisamos anexar o subcaminho <code>/v1/chat/completions</code> à URL do endpoint Hugging Face. O resultado final ficará assim:</p><pre><code>https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions</code></pre><p>Com isso pronto, podemos começar a programar em um notebook Python.</p><h4>Gerando a Chave API do Hugging Face</h4><p>Crie uma <a href="https://huggingface.co/join">conta Hugging Face</a> e obtenha um token de API seguindo <a href="https://huggingface.co/docs/hub/en/security-tokens#user-access-tokens">estas instruções</a>. Você pode escolher entre três tipos de token: <em>detalhado</em> (recomendado para produção, pois fornece acesso apenas a recursos específicos); <em>de leitura</em> (para acesso somente leitura); ou <em>de gravação</em> (para acesso de leitura e gravação). Para este tutorial, um token de leitura é suficiente, já que só precisamos chamar o endpoint de inferência. Guarde esta chave para o próximo passo.</p><h4>Configurando o endpoint de inferência do Elasticsearch</h4><p>Primeiro, vamos declarar um cliente Elasticsearch Python:</p><pre><code>os.environ["ELASTICSEARCH_API_KEY"] = "your-elasticsearch-api-key"
os.environ["ELASTICSEARCH_URL"] = "https://xxxx.us-central1.gcp.cloud.es.io:443"

es_client = Elasticsearch(
    os.environ["ELASTICSEARCH_URL"], api_key=os.environ["ELASTICSEARCH_API_KEY"]
)</code></pre><p>Em seguida, vamos criar um endpoint de inferência no Elasticsearch que use o modelo Hugging Face. Esse endpoint nos permitirá gerar respostas com base nos posts do blog e no prompt passado para o modelo.</p><pre><code>INFERENCE_ENDPOINT_ID = "smollm3-3b-pnz"

os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"] = (
 "https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions"
)
os.environ["HUGGING_FACE_API_KEY"] = "hf_xxxxx"

resp = es_client.inference.put(
        task_type="chat_completion",
        inference_id=INFERENCE_ENDPOINT_ID,
        body={
            "service": "hugging_face",
            "service_settings": {
                "api_key": os.environ["HUGGING_FACE_API_KEY"],
                "url": os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"],
            },
        },
    )</code></pre><h3>Conjunto de dados</h3><p>O conjunto de dados contém os <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/dataset.json">posts do blog</a> que serão consultados, representando um conjunto de conteúdo multilíngue usado em todo o fluxo de trabalho:</p><pre><code>// Articles dataset document example: 
{
    "id": "6",
    "title": "Complete guide to the new API: Endpoints and examples",
    "author": "Tomas Hernandez",
    "date": "2025-11-06",
    "category": "tutorial",
    "content": "This guide describes in detail all endpoints of the new API v2. It includes code examples in Python, JavaScript, and cURL for each endpoint. We cover authentication, resource creation, queries, updates, and deletion. We also explain error handling, rate limiting, and best practices. Complete documentation is available on our developer portal."
  }</code></pre><h4>Mapeamento do Elasticsearch</h4><p>Com o conjunto de dados definido, precisamos criar um esquema de dados que se ajuste adequadamente à estrutura do post do blog. Os seguintes <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mapeamentos de índice</a> serão usados para armazenar os dados no Elasticsearch:</p><pre><code>INDEX_NAME = "blog-posts"

mapping = {
    "mappings": {
        "properties": {
            "id": {"type": "keyword"},
            "title": {
                "type": "object",
                "properties": {
                    "original": {
                        "type": "text",
                        "copy_to": "semantic_field",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                    "translated_title": {
                        "type": "text",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                },
            },
            "author": {"type": "keyword", "copy_to": "semantic_field"},
            "category": {"type": "keyword", "copy_to": "semantic_field"},
            "content": {"type": "text", "copy_to": "semantic_field"},
            "date": {"type": "date"},
            "semantic_field": {"type": "semantic_text"},
        }
    }
}


es_client.indices.create(index=INDEX_NAME, body=mapping)</code></pre><p>Aqui, podemos ver com mais clareza como os dados são estruturados. Usaremos busca semântica para recuperar resultados baseados em linguagem natural, junto com a propriedade <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a> para copiar o conteúdo do campo para o campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_text</code></a>. Além disso, o campo <code>title</code> contém dois subcampos: o subcampo <code>original</code> armazena o título em inglês ou espanhol, dependendo do idioma original do artigo; e o subcampo <code>translated_title</code> está presente apenas para artigos em espanhol e contém a tradução para o inglês do título original.</p><h3>Ingestão de dados</h3><p>O seguinte trecho de código ingere o conjunto de dados de postagens do blog no Elasticsearch usando a <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/bulk_examples">bulk API</a>:</p><pre><code>def build_data(json_file, index_name):
    with open(json_file, "r") as f:
        data = json.load(f)

    for doc in data:
        action = {"_index": index_name, "_source": doc}
        yield action


try:
    success, failed = helpers.bulk(
        es_client,
        build_data("dataset.json", INDEX_NAME),
    )
    print(f"{success} documents indexed successfully")

    if failed:
        print(f"Errors: {failed}")
except Exception as e:
    print(f"Error: {str(e)}")</code></pre><p>Agora que os artigos já estão no Elasticsearch, precisamos criar uma função capaz de buscar no campo <code>semantic_text</code>:</p><pre><code>def perform_semantic_search(query_text, index_name=INDEX_NAME, size=5):
    try:
        query = {
            "query": {
                "match": {
                    "semantic_field": {
                        "query": query_text,
                    }
                }
            },
            "size": size,
        }

        response = es_client.search(index=index_name, body=query)
        hits = response["hits"]["hits"]

        return hits
    except Exception as e:
        print(f"Semantic search error: {str(e)}")
        return []</code></pre><p>Precisamos também de uma função que chame o endpoint de inferência. Neste caso, chamaremos o endpoint usando <strong><code>chat_completion</code> </strong>tipo de tarefa para obter respostas de streaming:</p><pre><code>def stream_chat_completion(messages: list, inference_id: str = INFERENCE_ENDPOINT_ID):
    url = f"{ELASTICSEARCH_URL}/_inference/chat_completion/{inference_id}/_stream"
    payload = {"messages": messages}
    headers = {
        "Authorization": f"ApiKey {ELASTICSEARCH_API_KEY}",
        "Content-Type": "application/json",
    }

    try:
        response = requests.post(url, json=payload, headers=headers, stream=True)
        response.raise_for_status()

        for line in response.iter_lines(decode_unicode=True):
            if line:
                line = line.strip()

                if line.startswith("event:"):
                    continue

                if line.startswith("data: "):
                    data_content = line[6:]

                    if not data_content.strip() or data_content.strip() == "[DONE]":
                        continue

                    try:
                        chunk_data = json.loads(data_content)

                        if "choices" in chunk_data and len(chunk_data["choices"]) > 0:
                            choice = chunk_data["choices"][0]
                            if "delta" in choice and "content" in choice["delta"]:
                                content = choice["delta"]["content"]
                                if content:
                                    yield content

                    except json.JSONDecodeError as json_err:
                        print(f"\nJSON decode error: {json_err}")
                        print(f"Problematic data: {data_content}")
                        continue

    except requests.exceptions.RequestException as e:
        yield f"Error: {str(e)}"</code></pre><p>Agora podemos escrever uma função que chama a função de busca semântica, junto com o endpoint de inferência <code>chat_completions</code> e o endpoint de recomendações, para gerar os dados que serão alocados nos cartões:</p><pre><code>def recommend_articles(search_query, index_name=INDEX_NAME, max_articles=5):
    print(f"\n{'='*80}")
    print(f"🔍 Search Query: {search_query}")
    print(f"{'='*80}\n")

    articles = perform_semantic_search(search_query, index_name, size=max_articles)

    if not articles:
        print("❌ No relevant articles found.")
        return None, None

    print(f"✅ Found {len(articles)} relevant articles\n")

    # Build context with found articles
    context = "Available blog articles:\n\n"
    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)
        context += f"Article {i}:\n"
        context += f"- Title: {source.get('title', 'N/A')}\n"
        context += f"- Author: {source.get('author', 'N/A')}\n"
        context += f"- Category: {source.get('category', 'N/A')}\n"
        context += f"- Date: {source.get('date', 'N/A')}\n"
        context += f"- Content: {source.get('content', 'N/A')}\n\n"

    system_prompt = """You are an expert content curator that recommends blog articles.

    Write recommendations in a conversational style starting with phrases like:
    - "If you're interested in [topic], this article..."
    - "This post complements your search with..."
    - "For those looking into [topic], this article provides..."


    FORMAT REQUIREMENTS:
    - Return ONLY a JSON array
    - Each element must have EXACTLY these three fields: "article_number", "title", "recommendation"
    - If the original title is in spanish, use the "translated_title" subfield in the "title" field

    Keep each recommendation concise (2-3 sentences max) and focused on VALUE to the reader.

    EXAMPLE OF CORRECT FORMAT:
    [
        {"article_number": 1, "title": "Article title in english", "recommendation": "If you are interested in [topic], this article provides..."},
        {"article_number": 2, "title": "Article title in english", "recommendation": " for those looking into [topic], this article provides..."}
    ]

    Return ONLY the JSON array following this exact structure."""

    user_prompt = f"""Search query: "{search_query}"

    Generate recommendations for the following articles: {context}
    """

    messages = [
        {"role": "system", "content": "/no_think"},
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ]

    # LLM generation
    print(f"{'='*80}")
    print("🤖 Generating personalized recommendations...\n")

    full_response = ""

    for chunk in stream_chat_completion(messages):
        print(chunk, end="", flush=True)
        full_response += chunk

    return context, articles, full_response</code></pre><p>Finalmente, precisamos extrair as informações e formatá-las para serem impressas:</p><pre><code>def display_recommendation_cards(articles, recommendations_text):
    print("\n" + "=" * 100)
    print("📇 RECOMMENDED ARTICLES".center(100))
    print("=" * 100 + "\n")

    # Parse JSON recommendations - clean tags and extract JSON
    recommendations_list = []
    try:

        # Clean up <think> tags
        cleaned_text = re.sub(
            r"<think>.*?</think>", "", recommendations_text, flags=re.DOTALL
        )
        # Remove markdown code blocks ( ... ``` or ``` ... ```)
        cleaned_text = re.sub(r"```(?:json)?", "", cleaned_text)
        cleaned_text = cleaned_text.strip()

        parsed = json.loads(cleaned_text)

        # Extract recommendations from list format
        for item in parsed:
            article_number = item.get("article_number")
            title = item.get("title", "")
            rec_text = item.get("recommendation", "")

            if article_number and rec_text:
                recommendations_list.append(
                    {
                        "article_number": article_number,
                        "title": title,
                        "recommendation": rec_text,
                    }
                )
    except json.JSONDecodeError as e:
        print(f"⚠️  Could not parse recommendations as JSON: {e}")
        return

    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)

        # Card border
        print("┌" + "─" * 98 + "┐")

        # Find recommendation and title for this article number
        recommendation = None
        title = None
        for rec in recommendations_list:
            if rec.get("article_number") == i:
                recommendation = rec.get("recommendation")
                title = rec.get("title")
                break

        # Print title
        title_lines = textwrap.wrap(f"📌 {title}", width=94)
        for line in title_lines:
            print(f"│  {line}".ljust(99) + "│")

        # Card border
        print("├" + "─" * 98 + "┤")

        # Print recommendation
        if recommendation:
            recommendation_lines = textwrap.wrap(recommendation, width=94)
            for line in recommendation_lines:
                print(f"│  {line}".ljust(99) + "│")

        # Card bottom
        print("└" + "─" * 98 + "┘")</code></pre><p>Vamos testar isso fazendo uma pergunta sobre as postagens do blog de segurança:</p><pre><code>search_query = "Security and vulnerabilities"

context, articles, recommendations = recommend_articles(search_query)

print("\nElasticsearch context:\n", context)

# Display visual cards
display_recommendation_cards(articles, recommendations)</code></pre><p>Aqui podemos ver os cartões no console gerados pelo fluxo de trabalho:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/730d35212594bb3db30447c3ea7e2a92857287b7-1999x1515.png" style="width: 100%;" /><p>Você pode ver os resultados completos, incluindo todos os acertos e a resposta do LLM, <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/results.md">neste arquivo</a>.</p><p>Estamos pedindo artigos relacionados a: &quot;Segurança e vulnerabilidades.&quot; Esta pergunta é usada como consulta de busca nos documentos armazenados no Elasticsearch. Os resultados recuperados são então passados para o modelo, que gera recomendações com base em seu conteúdo. Como podemos ver, o modelo fez um ótimo trabalho criando textos curtos envolventes que podem motivar o leitor a clicar.</p><h2>Conclusão</h2><p>Este exemplo mostra como Elasticsearch e Hugging Face podem ser combinados para criar um sistema centralizado rápido e eficiente para aplicações de IA. Essa abordagem reduz o esforço manual e oferece flexibilidade, graças ao extenso catálogo de modelos da Hugging Face. O uso do SmolLM3-3B, em particular, demonstra como modelos compactos e multilíngues ainda podem fornecer raciocínio significativo e geração de conteúdo quando combinados com busca semântica. Juntas, essas ferramentas oferecem uma base escalável e eficaz para construir análises inteligentes de conteúdo e aplicações multilíngues.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/recall-vector-search-quantization</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/recall-vector-search-quantization</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
            <pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/09f03b9239d66c36763cdab3fafcdac207ff6d83-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><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.</li><li><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.</li></ol><p>Mas a otimização geralmente vem acompanhada de uma taxa: <strong>a precisão</strong>.</p><p>O medo é válido: &quot;Se eu compactar meus dados e usar atalhos durante a busca, perderei os melhores resultados?&quot; &quot;Essa otimização degrada a relevância do meu mecanismo de busca?&quot;</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> 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><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).</li><li><strong>Busca exata (plana):</strong> às vezes chamada de método &quot;força bruta&quot;. O mecanismo de busca analisa cada documento em um índice e calcula a distância.<ul><li><em>Prós:</em> recall perfeito de 100%.</li><li><em>Contras:</em> computacionalmente caro e lento em larga escala.</li></ul></li><li><strong>Busca aproximada (HNSW):</strong> O método do &quot;atalho&quot;. 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.<ul><li><em>Prós:</em> extremamente rápido e escalável.</li><li><em>Contras:</em> Você pode perder um vizinho se a travessia do gráfico parar muito cedo.</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 &quot;Filme&quot;. 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><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.</li><li><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.</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>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.</li><li>E a busca <strong>aproximada (HNSW)</strong> retorna IDs [1, 2, 3... 9, 99].</li><li>Encontramos corretamente nove dos 10 principais. A pontuação é <strong>0,9</strong>.</li></ul><p>Aqui está o mapeamento que usamos:</p><pre><code># 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"
        }
    }
}</code></pre><p><strong>Os resultados: a &quot;estagnação&quot; 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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Documentos</td><td style="border: 1px solid #ccc; padding: 4px;">Recall@10 score</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">1.000</td><td style="border: 1px solid #ccc; padding: 4px;">1.000 (100%)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,998 (100%)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,992 (99,4%)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">20.000</td><td style="border: 1px solid #ccc; padding: 4px;">0,999 (99,0%)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">40.000</td><td style="border: 1px solid #ccc; padding: 4px;">0.992 (98,8%)</td>
                    </tr>
              </tbody>
            </table>
          <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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4bfacb1d0cce6fdf6df0e1a9d4fc5d4007a66da-1999x1209.png" style="width: 100%;" /><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><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.</li><li><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.</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>
        </item>
        <item>
            <title><![CDATA[A extensão Gemini CLI para Elasticsearch com ferramentas e recursos]]></title>
            <description><![CDATA[Apresentamos a extensão da Elastic para a CLI Gemini do Google, que permite buscar, extrair e analisar dados do Elasticsearch em fluxos de trabalho de desenvolvedores e agentes.
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/gemini-cli-extension-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/gemini-cli-extension-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Walter Rafelsberger, Enrico Zimuel]]></dc:creator>
            <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Temos a satisfação de anunciar o lançamento da nossa extensão Elastic para a Gemini CLI do Google, trazendo toda a eficiência da <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> e <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a> diretamente para o seu fluxo de trabalho de desenvolvimento de IA. Essa extensão também oferece várias habilidades de agente recentemente desenvolvidas para interagir com o Elasticsearch.</p><p>A extensão está disponível como um projeto open source <a href="https://github.com/elastic/gemini-cli-elasticsearch">aqui</a>.</p><h2>O que é a Gemini CLI e como você a instala?</h2><p><a href="https://geminicli.com/">Gemini CLI</a> é um agente de IA open source que traz os modelos Gemini do Google diretamente para a linha de comando. Ele permite que os desenvolvedores interajam com a IA a partir do terminal para realizar tarefas como gerar código, editar arquivos, executar comandos do shell e recuperar informações da web.</p><p>Diferentemente das interfaces típicas de chat, a Gemini CLI se integra ao seu ambiente local de desenvolvimento, o que significa que ela pode entender o contexto do projeto, modificar arquivos, executar builds ou testes e automatizar fluxos de trabalho diretamente no terminal. Isso a torna útil para desenvolvedores, engenheiros de confiabilidade de sites (SREs) e outros profissionais que desejam codificação e automação assistidas por IA sem sair do fluxo de trabalho da linha de comando.</p><p>Você pode instalar a Gemini CLI usando vários gerenciadores de pacotes. O método mais comum é usar o npm:</p><pre><code>npm install -g @google/gemini-cli</code></pre><p>Para conhecer opções alternativas de instalação, consulte a <a href="https://geminicli.com/docs/get-started/installation/">página oficial de instalação</a>.</p><p>Após a instalação, inicie a CLI executando:</p><pre><code>gemini</code></pre><p>Você vê uma tela, conforme mostrado na Figura 1:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" style="width: 100%;" /><h2>Configurar o Elasticsearch</h2><p>Precisamos ter uma instância do Elasticsearch em execução. Se quiser usar o servidor MCP (Model Context Protocol), você também precisará ter o Kibana 9.3+ instalado. Para usar a habilidade da Elasticsearch linguagem de consulta (ES|QL) (<code>esql</code>) descrita abaixo, o Kibana não é necessário.</p><p>Você pode ativar um teste gratuito no <a href="https://www.elastic.co/cloud">Elastic Cloud</a> ou instalá-lo localmente usando o script <a href="https://github.com/elastic/start-local"><code>start-local</code></a> :</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Isso instalará o Elasticsearch e o Kibana no seu computador e gerará uma chave API para ser usada na configuração da Gemini CLI.</p><p>A chave API será mostrada como saída do comando anterior e armazenada em um <strong>.env</strong> arquivo na pasta <strong><code>elastic-start-local</code></strong>.</p><p>Se você está usando o Elasticsearch no local (por exemplo, usando <code>start-local</code>), e quer usar o Elastic Agent Builder com MCP, também precisa conectar um grande modelo de linguagem (LLM). Você pode ler <a href="https://www.elastic.co/docs/explore-analyze/ai-features/llm-guides/llm-connectors">esta página de documentação</a> para entender as diferentes opções.</p><p>Se você estiver usando o Elastic Cloud (ou serverless), já tem uma conexão LLM pré-configurada.</p><h2>Instale a extensão do Elasticsearch</h2><p>Você pode instalar a extensão Elasticsearch para Gemini CLI com o seguinte comando:</p><pre><code>gemini extensions install https://github.com/elastic/gemini-cli-elasticsearch</code></pre><p>Você pode verificar se as extensões foram instaladas com sucesso abrindo o Gemini e executando o seguinte comando:</p><pre><code>/extensions list</code></pre><p>Você deverá ver a extensão Elasticsearch disponível.</p><p>Se quiser usar a integração MCP, precisa ter uma versão do Elasticsearch 9.3+ instalada. Você precisa da URL do seu servidor MCP do <a href="https://www.elastic.co/kibana">Kibana</a>:</p><ul><li>Obtenha a URL do seu servidor MCP em Agents &gt; View all tools &gt; Manage MCP &gt; Copy MCP Server URL (Agentes &gt; Ver todas as ferramentas &gt; Gerenciar MCP &gt; Copiar URL do Servidor MCP).</li><li>A URL ficará assim: https://your-kibana-instance/api/agent_builder/mcp</li></ul><p>Você precisa da URL do endpoint do Elasticsearch. Isso normalmente é relatado no topo da página do Kibana Elasticsearch. Se você está rodando o Elasticsearch com <code>start-local</code>, você já tem o endpoint na chave <code>ES_LOCAL_URL</code> no <code>start-local</code> .env. arquivo.</p><p>Também é necessário uma chave de API. Se estiver executando o Elasticsearch com <code>start-local</code>, você já tem a <code>ES_LOCAL_API_KEY</code> no arquivo<code>start-local</code> .env arquivo. Caso contrário, é possível criar uma chave de API usando a interface do Kibana, conforme <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">indicado aqui</a>:</p><ul><li>No Kibana: Stack Management &gt; Security &gt; API Keys &gt; Create API Key (Stack management &gt; Segurança &gt; Chaves de API &gt; Criar chave de API) .</li><li>Sugerimos definir apenas os privilégios de leitura para a chave API, habilitando o privilégio <code>feature_agentBuilder.read</code> conforme <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/permissions#grant-access-with-roles">reportado aqui</a>.</li><li>Copie o valor da chave de API codificada.</li></ul><p>Defina as variáveis de ambiente necessárias no seu shell:</p><pre><code>export ELASTIC_URL="your-elasticsearch-url"
export ELASTIC_MCP_URL="your-elasticsearch-mcp-url"
export ELASTIC_API_KEY="your-encoded-api-key"</code></pre><h2>Instale o conjunto de dados de exemplo</h2><p>Você pode instalar o conjunto de dados de <strong>pedidos de comércio eletrônico </strong>disponível no Kibana. Inclui um único índice chamado <strong><code>kibana_sample_data_ecommerce</code></strong>, contendo informações sobre 4.675 pedidos de um website. Para cada pedido, temos as seguintes informações:</p><ul><li>Informações do cliente (nome, ID, data de nascimento, e-mail e mais).</li><li>Data do pedido.</li><li>ID do pedido.</li><li>Produtos (lista de todos os produtos com preço, quantidade, ID, categoria, desconto e outros detalhes).</li><li>SKU.</li><li>Preço total (sem impostos, com impostos).</li><li>Quantidade total.</li><li>Informações geográficas (cidade, país, continente, localização, região).</li></ul><p>Para instalar os dados de exemplo, abra a página <strong>Integrações</strong> no Kibana (busque por “Integração” na barra de busca superior) e instale os <strong>Dados de Exemplo</strong>. Para mais detalhes, consulte a <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">documentação aqui</a>.</p><p>O objetivo deste artigo é mostrar como é fácil configurar o Gemini CLI para se conectar ao Elasticsearch e interagir com o índice <strong><code>kibana_sample_data_ecommerce</code></strong>.</p><h2>Como usar o Elasticsearch MCP</h2><p>Você pode verificar a conexão usando o seguinte comando no Gemini:</p><pre><code>/mcp list</code></pre><p>Você deve ver o <strong><code>elastic-agent-builder</code></strong> ativado, como mostrado na Figura 2:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1508423bc1d1bc3c04a1cb01e2d59495a3516ed1-1465x844.png" style="width: 100%;" /><p>O Elasticsearch fornece um conjunto padrão de ferramentas. Veja a descrição <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/builtin-tools-reference">aqui</a>.</p><p>Usando essas ferramentas, você pode interagir com o Elasticsearch, fazendo perguntas como:</p><ul><li><code>Give me the list of all the indexes available in Elasticsearch.</code></li><li><code>How many customers are based in the USA in the kibana_sample_data_ecommerce index of Elasticsearch?</code></li></ul><p>Dependendo da pergunta, o Gemini usará uma ou mais ferramentas disponíveis para tentar respondê-la.</p><h2>Os comandos /elastic</h2><p>Na extensão Elasticsearch para Gemini CLI, também adicionamos<strong> <code>/elastic</code></strong> comandos.</p><p>Se você executar o comando <strong><code>/help</code></strong>, verá todas as opções de <code>/elastic</code> disponíveis (Figura 3):</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5b2a0727ce7a04354878dd048253d3f4d062324b-1983x230.png" style="width: 100%;" /><p>Esses comandos podem ser úteis se você quiser executar diretamente uma ferramenta específica do servidor MCP.<code>elastic-agent-builder</code>&nbsp; Por exemplo, usando o seguinte comando, você pode obter o mapeamento do <code>kibana_sample_data_ecommerce</code>:</p><pre><code>/elastic:get-mapping kibana_sample_data_ecommerce</code></pre><p>Esses comandos são essencialmente atalhos para executar ferramentas específicas, em vez de depender do modelo Gemini para determinar qual ferramenta deve ser usada.</p><h2>Como usar as habilidades do Elasticsearch</h2><p>Essa extensão também inclui uma <a href="https://github.com/elastic/gemini-cli-elasticsearch/tree/main/skills/esql">habilidade de agente para o ES|QL</a>, a <a href="https://www.elastic.co/docs/explore-analyze/discover/try-esql">Linguagem de Consulta Elasticsearch</a> disponível no Elasticsearch. <a href="https://agentskills.io/home">Agent Skills</a> é um formato aberto que fornece aos agentes de programação de IA, como o Gemini CLI, instruções personalizadas para tarefas específicas. Eles utilizam um conceito chamado <em>divulgação progressiva</em>, o que significa que apenas uma breve descrição da habilidade é adicionada ao prompt inicial do sistema. Quando você solicita que o agente execute uma tarefa, como consultar o Elasticsearch, ele associa a solicitação à habilidade relevante e carrega dinamicamente as instruções detalhadas. Essa é uma forma eficiente de gerenciar orçamentos de tokens enquanto fornece à IA exatamente o contexto que ela precisa.</p><p>A<strong> habilidade</strong> <strong><code>esql</code></strong>foi projetada para permitir que o Gemini CLI escreva e execute consultas ES|QL diretamente no seu cluster. ES|QL é uma poderosa linguagem de consulta encadeada que torna a exploração de dados, a análise de logs e as agregações altamente intuitivas. Com essa habilidade ativada, você não precisa pesquisar a sintaxe ES|QL; basta fazer perguntas em linguagem natural ao Gemini CLI sobre seus dados e o agente cuidará do resto.</p><p>As execuções são realizadas usando simples comandos <a href="https://curl.se/">curl</a> executados em um terminal. Isso é possível porque o Elasticsearch oferece um conjunto abrangente de APIs REST que podem ser facilmente usadas para integrar o sistema a qualquer arquitetura.</p><p><strong>O que a  habilidadeesqloferece:</strong></p><ul><li><strong>Descoberta de índices e esquemas:</strong> o agente pode usar as ferramentas integradas da habilidade para listar os índices disponíveis e buscar mapeamentos de campo. Por exemplo, antes de escrever uma consulta para o conjunto de dados de comércio eletrônico, o agente pode executar uma verificação de esquema em <strong><code>kibana_sample_data_ecommerce</code></strong> para entender os campos disponíveis, como <strong><code>taxful_total_price</code></strong> ou <strong><code>category</code></strong>.</li><li><strong>Tradução perfeita da linguagem natural:</strong> a habilidade dá ao agente mais do que um simples manual de referência; ela fornece um guia específico para interpretar a intenção do usuário. Quando você digita solicitações em linguagem natural, como &quot;Mostrar tempo médio de resposta agrupado por serviço&quot;, o agente usa o padrão de correspondência da habilidade para traduzir instantaneamente suas palavras nas agregações, filtros e comandos ES|QL corretos.</li><li><strong>Autocorreção:</strong> Se uma consulta falhar (por exemplo, devido a uma incompatibilidade de tipo ou erro de sintaxe), a skill retorna a consulta gerada juntamente com a mensagem de erro exata do Elasticsearch, permitindo que o agente corrija instantaneamente a consulta e tente novamente sem que você precise intervir.</li></ul><p>Como a habilidade <code>esql</code> também está disponível como ferramenta no servidor MCP <code>elastic-agent-builder</code>, precisamos desativar esse servidor momentaneamente. Você pode usar o seguinte comando para desativá-lo:</p><pre><code>/mcp disable elastic-agent-builder</code></pre><p>Em seguida, você pode simplesmente digitar um prompt como esse em sua Gemini CLI:</p><pre><code>Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index</code></pre><p>O agente irá:</p><ul><li>Reconheça a necessidade da habilidade <code>esql</code> .</li><li>Verifique o esquema do <strong><code>kibana_sample_data_ecommerce</code></strong>.</li><li>Crie uma consulta ES|QL, como: <code>FROM kibana_sample_data_ecommerce | STATS total_revenue = SUM(taxful_total_price) BY category.keyword | SORT total_revenue DESC | LIMIT 5</code>.</li><li>Execute a consulta na API do Elasticsearch.</li><li>Apresente a resposta final para você diretamente no terminal.</li></ul><p>Aqui, apresentamos um exemplo de resposta do Gemini ao prompt anterior:</p><pre><code>───────────────────────────────────────────────────────────
> Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index   ───────────────────────────────────────────────────────────
✓  Activate Skill "esql": Interact with Elasticsearch using ES|QL and curl. Use when querying, managing indices,  …
Skill esql activated. Resources loaded from /home/user/.gemini/extensions/elasticsearch/skills/esql: 
Showing up to 200 items (files + folders).                                                                     /home/user/.gemini/extensions/elasticsearch/skills/esql/                                                            ├───SKILL.md 
└───references/
├───esql-reference.md 
├───generation-tips.md 
└───query-patterns.md

✦ I will start by exploring the mapping of the kibana_sample_data_ecommerce index to identify the fields for product category and sales revenue. Then, I will construct and execute an ES|QL query to determine the top 5 product categories by total sales revenue.

✓  Shell curl -s "${ELASTIC_URL%/}/kibana_sample_data_ecommerce/_mapping" -H "Authorization: ApiKey $(printenv ELAS…                                                                                                                       {  
   "kibana_sample_data_ecommerce": { 
     "mappings": { 
       "properties": {
         "category": { 
           "type": "text", 
           "fields": {
             "keyword": {
               "type": "keyword"
             }
           }
         }, 
         "currency": {
           "type": "keyword"
         }, 

✦ Okay, I have the mapping. The category is products.category.keyword and sales revenue is taxful_total_price. I'll now execute the ES|QL query to get the top 5 categories by revenue.

✓  Shell curl -s -X POST "${ELASTIC_URL%/}/_query" \  -H "Authorization: ApiKey $(printenv ELASTIC_API_KEY)" \  -H …
{ 
   "took": 62,
   "is_partial": false, 
   "completion_time_in_millis": 1772805644164,
   "documents_found": 4675,
   "values_loaded": 12084, 
   "start_time_in_millis": 1772805644102, 
   "expiration_time_in_millis": 1773237644087,  
   "columns": [ 
     { 
       "name": "total_revenue",    
       "type": "double"  
     }, 
     {   
       "name": "products.category.keyword",

✦ The query for top 5 product categories by revenue was successful. I'll format the returned JSON values into a   user-friendly list.

 ✓  Shell 
                             
 Top 5 product categories by total sales revenue: 
 1. Men's Clothing: 149393.91
 2. Women's Clothing: 135099.91
 3. Women's Shoes: 105479.17
 4. Men's Shoes: 91797.92
 5. Women's Accessories: 60830.31 

✦ I've found the answer; now I'll format it for clarity.

 ✓  Shell 

 Here are the top 5 product categories by total sales revenue:                                                        
                                                                      
 | Rank | Category              | Total Revenue | 
 |------|-----------------------|---------------| 
 | 1    | Men's Clothing        | 149,393.91    |
 | 2    | Women's Clothing      | 135,099.91    |
 | 3    | Women's Shoes         | 105,479.17    |
 | 4    | Men's Shoes           | 91,797.92     | 
 | 5    | Women's Accessories   | 60,830.31     | 

✦ I've formatted and presented the answer. I'm finished.
 ───────────────────────────────────────────────────────────</code></pre><p>É interessante notar como o modelo Gemini gera a resposta final mostrando todos os passos que ele segue. Aqui, você pode ver claramente a influência da habilidade no processo de raciocínio do modelo. Na primeira vez que o modelo reconhece que precisa usar uma habilidade ou executar um comando shell, ele solicita permissão usando a abordagem baseada em intervenção humana.</p><p>Ao lidar com o trabalho pesado de descoberta de esquema, geração de consultas e execução, a habilidade <code>esql</code> permite que você se concentre inteiramente nas respostas, em vez da mecânica de obtê-las. Você obterá os dados de que precisa, formatados corretamente e diretamente no seu terminal, tudo isso sem precisar escrever uma única linha de código ou alternar para outro aplicativo.</p><h2>Conclusão</h2><p>Neste artigo, apresentamos a extensão Elasticsearch para Gemini CLI que lançamos recentemente. Essa extensão oferece a você a capacidade de interagir com a instância do Elasticsearch usando o Gemini e o servidor Elasticsearch MCP fornecido pelo Elastic Agent Builder, disponível a partir da versão 9.3.0, bem como o comando <code>/elastic</code>.</p><p>Além disso, a extensão também inclui uma habilidade <code>esql</code> que converte a solicitação do usuário de linguagem natural em uma consulta ES|QL. Essa habilidade pode ser particularmente útil quando o servidor MCP não pode ser usado, pois a comunicação subjacente é conduzida por comandos curl simples executados em um terminal. O Elasticsearch oferece um conjunto abrangente de APIs REST que podem ser facilmente integradas a qualquer projeto. Isso é especialmente útil ao desenvolver aplicações de IA agêntica.</p><p>Para mais informações sobre nossa extensão Gemini CLI, acesse o repositório do projeto <a href="https://github.com/elastic/gemini-cli-elasticsearch">aqui</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Habilidades de agentes para Elastic: transforme agentes de IA em especialistas Elastic]]></title>
            <description><![CDATA[Dê ao seu agente de codificação de IA o conhecimento necessário para consultar, visualizar, proteger e automatizar com o Elastic Agent Skills.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agent-skills-elastic</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agent-skills-elastic</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Graham Hudgins, Matt Ryan]]></dc:creator>
            <pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/09e64953819083168a9ecef0888c7f8bde1a43bd-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Todo desenvolvedor, engenheiro de confiabilidade de sites (SRE) ou analista que tentou usar um agente de codificação de IA com uma Platform especializada encontrou a mesma barreira. Você pede para o agente escrever uma consulta, configurar um alerta ou investigar algo, e ele chega perto, mas não acerta. A Elastic tem uma vantagem: mais de uma década de documentação, postagens em blogs e respostas da comunidade significa que os agentes de IA já conhecem a Elastic melhor do que a maioria das plataformas de dados. Mas essa profundidade vem com ruído. APIs obsoletas ficam ao lado das atuais. Padrões desatualizados têm uma classificação tão alta quanto práticas recomendadas. O agente reproduz com confiança uma abordagem que funcionou três versões atrás, porque nos seus dados de treinamento, funcionou. O resultado é um imposto de correção: os usuários inserem manualmente a documentação no contexto, corrigem a sintaxe alucinada e contornam o agente em vez de trabalharem com ele. Pior ainda, capacidades avançadas ficam completamente sem uso, não porque os usuários não precisem delas, mas porque o agente não sabe que elas existem.</p><p>Por isso, estamos tornando o <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills</a> open source: expertise nativa na plataforma para Elasticsearch, Kibana, Elastic observabilidade e Elastic Security. Adicione ao runtime do agente que você já usa e evolua seu agente de um &quot;generalista&quot; que precisa adivinhar muita sintaxe para um especialista com expertise real, como a capacidade de usar muitos dos padrões arquitetônicos das próprias equipes de engenharia da Elastic. Esta versão inicial de prévia técnica foca em habilidades com máxima compatibilidade com o <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>, mas evoluirá logo para incluir suporte aprimorado para versões anteriores da plataforma.</p><p>Além disso, a Elastic está resolvendo esse problema dos dois lados. Para agentes na plataforma Elastic, o <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Elastic Agent Builder</a> (agora disponível de forma geral) permite que você crie e converse com agentes de IA que herdam os controles de acesso aos seus dados, usem ferramentas integradas de busca e análise e trabalhem em contexto junto com seus dashboards, alertas e investigações. Estamos trabalhando muito para garantir experiências incríveis e agêntica na plataforma Elastic. Mas nem todo agente vive dentro da Elastic. Sua equipe já usa Cursor, Claude Code ou outros tempos de execução, e esses agentes também precisam usar a Elastic da maneira certa. É aí que entra o Agent Skills.</p><h2>Por que os agentes enfrentam dificuldades com plataformas especializadas</h2><p>Grandes modelos de linguagem (LLMs) são generalistas muito capazes. Eles podem escrever Python, explicar manifestos do Kubernetes e refatorar componentes do React porque os dados de treinamento são ricos em exemplos. Mas quando se trata de trabalho específico de plataforma, do tipo que envolve linguagens de consulta proprietárias, superfícies profundas de API e práticas recomendadas específicas do domínio, eles ficam aquém de maneiras previsíveis.</p><p>Para o Elasticsearch, a lacuna aparece concretamente:</p><ul><li><strong>A linguagem de consulta do Elasticsearch (ES|QL) é um território novo.</strong> Os LLMs são treinados em SQL, mas o ES|QL é uma linguagem de consulta baseada em pipes com sintaxe diferente, funções distintas e semântica distinta. Os agentes escrevem consultas que parecem plausíveis, mas não são analisadas. Eles confundem <code>WHERE</code> com <code>| WHERE</code>, inventam funções que não existem e ignoram o modelo de composição baseado em pipes.</li><li><strong>As interfaces de API são amplas e abrangentes.</strong> Elasticsearch, Kibana e Elastic Security expõem centenas de APIs em busca, ingestão, alertas, regras de detecção, gerenciamento de casos, dashboards e muito mais. Um agente armado apenas com dados de treinamento gerais precisa adivinhar qual endpoint chamar, como é o corpo da solicitação e como lidar com a resposta. Ele erra com frequência suficiente para acabar com a confiança.</li><li><strong>Práticas recomendadas não estão nos dados de treinamento.</strong> Quando você deve usar <code>semantic_text</code> em vez de um pipeline de embedding personalizado? Como você deve estruturar um pipeline de ingestão para um CSV de 10GB? Qual é a sintaxe correta da regra de detecção para uma técnica <a href="https://www.elastic.co/docs/solutions/security/detect-and-alert/mitre-attandckr-coverage">MITRE ATT&amp;CK®</a>? Agentes de uso geral não têm conhecimento curado e estruturado de forma confiável e específico para Elastic, carregado por padrão. Eles teriam que procurar, e mesmo que encontrassem, documentos brutos nem sempre codificam as decisões e práticas recomendadas que profissionais habilidosos utilizam.</li></ul><p>O resultado: os desenvolvedores passam mais tempo corrigindo a saída do agente do que passariam escrevendo o código eles mesmos. Essa é a experiência que ninguém esperava.</p><h2>Habilidades de agentes: conhecimento de plataforma, empacotado para agentes</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8d16ec00d16e70a916c5eef0aaa23fcc735b7186-1067x1280.png" style="width: 100%;" /><p>As habilidades do agente são diretórios independentes de instruções, scripts e material de referência que os tempos de execução do agente podem carregar de forma dinâmica. Quando uma habilidade está ativa, o agente tem acesso ao contexto certo na hora certa: sintaxe de consulta, padrões de API, lógica de validação, exemplos trabalhados, para que ele possa completar as tarefas corretamente na primeira tentativa.</p><p>Cada habilidade segue a especificação aberta do <a href="https://agentskills.io">agentskills.io</a>: uma pasta com um arquivo <code>SKILL.md</code> contendo metadados e instruções estruturadas. Nenhum formato proprietário, sem bloqueio. As habilidades funcionam em diferentes ambientes de execução de agentes, incluindo Cursor, Claude Code, GitHub Copilot, Windsurf, Gemini CLI, Cline, Codex e <a href="https://agentskills.io">muitos outros</a>.</p><h3>O que há na versão inicial v0.1.0?</h3><p>O primeiro conjunto de habilidades abrange cinco áreas do Elastic Stack:</p><ul><li>Interação com APIs do Elasticsearch (busca, indexação, clustering)</li><li>Crie e gerencie conteúdo do Kibana, como dashboards, alertas, conectores e muito mais</li><li>Especialização em Elastic Observability</li><li>Conhecimento especializado para o Elastic Security</li><li>Criando agentes eficazes no Agent Builder</li></ul><h3>As habilidades podem ser combinadas</h3><p>Habilidades não são monolíticas. Eles são modulares por natureza. Seu agente carrega apenas as habilidades relevantes para a tarefa em questão. Trabalhando em uma consulta ES|QL? A habilidade ES|QL é ativada. Precisa criar um dashboard a partir desses resultados? A habilidade do dashboard melhora. Está avaliando a integridade do seu aplicativo? A habilidade de avaliação de serviço entra em jogo. Está investigando um alerta de segurança? A triagem se conecta às habilidades de gerenciamento de casos e resposta à medida que a investigação avança.</p><p>Essa capacidade de composição significa que você não precisa de um único e enorme prompt que tente cobrir tudo. Cada habilidade carrega exatamente o contexto que seu domínio exige, nada mais, nada menos.</p><h2>Para desenvolvedores que criam aplicativos de pesquisa e IA</h2><p>Se você está carregando dados no Elasticsearch, escrevendo consultas ou migrando índices, as habilidades reduzem o ciclo de geração de código, ocorrência de erros e busca nos documentos para descobrir o que deu errado.</p><p>Peça ao seu agente para carregar um arquivo CSV. Ele usará uma ferramenta de ingestão de streaming que gerencia a contrapressão e infere mapeamentos a partir dos dados. Não é um loop _bulk feito manualmente que executa e fica sem memória ao processar o primeiro arquivo grande. Peça para ele consultar o ES|QL e descobrir os nomes reais de índice e esquemas de campos, escrever consultas válidas com sintaxe correta, fazer agregações apropriadas e seleção de recursos compatível com a versão, em vez de dar um palpite no estilo SQL que exige três rodadas de depuração. Ao solicitar a reindexação em todos os clusters, o sistema segue todo o fluxo de trabalho operacional: cria o destino com mapeamentos explícitos, ajusta as configurações para otimizar a taxa de transferência, executa a tarefa de forma assíncrona e restaura as configurações de produção ao terminar, em vez de chamar o método _reindex, que omite metade das etapas que um operador experiente seguiria.</p><p>Em vez de um agente que te dá um ponto de partida plausível que você precisa corrigir, você tem um que codifica a disciplina operacional que faz a saída funcionar.</p><p><strong>Exemplos de impactos do uso das Habilidades do Elastic Agent</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Eval</td><td style="border: 1px solid #ccc; padding: 4px;">O que a habilidade alterou</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-audit-query-failed-logins</td><td style="border: 1px solid #ccc; padding: 4px;">Usou os padrões de consulta do log de auditoria da funcionalidade em vez de busca genérica</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-authz-role-mapping-ldap</td><td style="border: 1px solid #ccc; padding: 4px;">Emitiu a estrutura correta de chamadas de API para mapeamento de funções</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-basic-query</td><td style="border: 1px solid #ccc; padding: 4px;">Criou a sintaxe de pipe ES|QL no Query DSL</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-error-handling</td><td style="border: 1px solid #ccc; padding: 4px;">Priorize o esquema em vez de tentar adivinhar os nomes dos campos</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">esql-schema-discovery</td><td style="border: 1px solid #ccc; padding: 4px;">Nunca adivinhou um nome de índice</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-ingest-csv-with-infer</td><td style="border: 1px solid #ccc; padding: 4px;">Usou --infer-mappings sozinho, evitou combinar com --source-format cvs, o que causa um índice vazio</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-ingest-json-file</td><td style="border: 1px solid #ccc; padding: 4px;">Abordagem robusta de ingestão utilizada, capaz de lidar com arquivos grandes</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-reindex-local-async</td><td style="border: 1px solid #ccc; padding: 4px;">O índice de destino foi criado primeiro com réplicas: 0 e refresh_interval: "-1", depois foi feita a reindexação assíncrona. A referencia ignorou a preparação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">es-security-403-privileges</td><td style="border: 1px solid #ccc; padding: 4px;">Segui o fluxo de trabalho de diagnóstico da habilidade para erros de privilégio em vez de conselhos genéricos</td>
                    </tr>
              </tbody>
            </table>
          <h2>Para equipes de segurança</h2><p>As equipes de segurança repetem os mesmos fluxos de trabalho operacionais diariamente: triagem de alertas, ajuste de regras de detecção e gerenciamento de casos. As habilidades do agente codificam esse conhecimento processual para que seu agente de IA possa executar esses fluxos de trabalho corretamente, chamando as APIs certas na ordem certa com os nomes de campo corretos. Para um guia prático que leva você do zero a um ambiente de Elastic Security totalmente povoado sem sair do seu IDE, consulte <a href="https://www.elastic.co/security-labs/agent-skills-elastic-security">Comece a usar o Elastic Security a partir do seu agente de IA</a>.</p><h2>Para equipes de observabilidade e operações</h2><p>O novo Agent Skills for Elastic Observability reduz o trabalho operacional de instrumentar sistemas complexos, gerenciar SLOs, analisar dados complexos e avaliar a integridade dos serviços. A incorporação da expertise nativa da Elastic diretamente aos agentes de IA permite que as equipes executem fluxos de trabalho complexos de observabilidade usando uma linguagem natural simples. Isso permite que as equipes de SREs e operações resolvam incidentes com mais rapidez e mantenham sistemas confiáveis com mais facilidade. Saiba mais no <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">blog</a>.</p><h2>Open source, especificações abertas, impulsionado pela comunidade</h2><p>Estamos lançando o Agent Skills sob a licença Apache 2.0 porque acreditamos que o conhecimento dos agentes deve ser aberto. A especificação <a href="https://agentskills.io">agentskills.io</a> que as habilidades seguem é um padrão aberto, não um formato proprietário da Elastic. Queremos que as habilidades sejam um esforço comunitário e não um privilégio isolado.</p><h2>Parte de um panorama maior</h2><p>O Agent Skills é uma parte de uma iniciativa mais ampla para tornar o Elasticsearch a plataforma de dados mais amigável para agentes disponível. Para agentes que residem na plataforma Elasticsearch, o <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Agent Builder</a> vai além, herdando os controles de acesso e permissões dos seus dados, fornecendo ferramentas integradas e personalizadas para pesquisa e análise, e permitindo que os usuários interajam com os agentes em contexto, juntamente com dashboards, alertas e investigações. Por fim, o suporte para habilidades chegará em breve ao Agent Builder, permitindo que o desenvolvedor tenha flexibilidade para aproveitar o Elastic Agent Skills, assim como as habilidades de qualquer outra fonte, para viabilizar chat seguro, com mais contexto e automação na plataforma Elasticsearch.</p><p>Para os agentes que residem em outros locais, estamos investindo no ecossistema aberto:</p><ul><li><strong>Expansão do servidor Model Context Protocol (MCP):</strong> ampliação do <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">endpoint MCP</a> no Agent Builder com mais ferramentas além das operações atuais de busca, ES|QL e indexação.</li><li><strong>Melhorias na autenticação:</strong> facilitar a conexão segura dos agentes, com o objetivo de eliminar a necessidade de copiar e colar manualmente as chaves de API.</li><li><strong>Documentação legível por LLM:</strong> publicar arquivos <code>llms.txt</code> e <code>AGENTS.md</code> para que os agentes possam descobrir e entender as APIs da Elastic por conta própria.</li><li><strong>Uma interface de linha de comando (CLI) para fluxos de trabalho de agentes:</strong> ferramentas de linha de comando que facilitam o gerenciamento de conexões e as operações comuns dos agentes.</li></ul><p>Habilidades são a camada que você pode usar hoje. O restante está chegando.</p><h2>Começar</h2><p><strong>Antes de começar: </strong>os agentes de codificação de IA operam com credenciais reais, acesso real ao shell e com todas as permissões do usuário que os executa. Quando esses agentes são direcionados para fluxos de trabalho de segurança, os riscos são maiores: você está entregando a um sistema automatizado o acesso à lógica de detecção, ações de resposta e telemetria sensível. Cada perfil de risco de organização é diferente. Antes de habilitar fluxos de trabalho de segurança orientados por IA, <strong>avalie quais dados o agente pode acessar, quais ações ele pode realizar e o que acontece se ele se comportar de forma inesperada</strong>.</p><p>Instale o Elastic Agent Skills no tempo de execução do seu agente:</p><p><code>npx skills add elastic/agent-skills</code></p><p>Isso detecta automaticamente os runtimes instalados do agente e posiciona as habilidades no diretório de configuração correto. A partir daí, seu agente os coleta automaticamente.</p><p>Você também pode acessar diretamente o <a href="https://github.com/elastic/agent-skills">catálogo de habilidades</a> e instalar habilidades individuais manualmente, copiando a pasta de habilidades para o diretório de configuração do agente.</p><p>Ainda não tem um cluster Elasticsearch? Inicie uma <a href="https://cloud.elastic.co/registration">avaliação gratuita do Elastic Cloud</a>. Leva cerca de um minuto para obter um ambiente totalmente configurado.</p><p><strong>Explorar o projeto:</strong></p><ul><li><a href="https://github.com/elastic/agent-skills">Repositório de habilidades do agente</a></li><li><a href="https://agentskills.io">Especificação agentskills.io</a></li><li><a href="https://www.elastic.co/docs">Documentação do Elasticsearch</a></li><li><a href="https://cloud.elastic.co/registration">Avaliação gratuita do Elastic Cloud</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Resolução de entidades com Elasticsearch, parte 4: O desafio definitivo]]></title>
            <description><![CDATA[Resolvendo e avaliando desafios de resolução de entidades em um conjunto de dados de desafio definitivo altamente diversificado, projetado para evitar atalhos.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/entity-resolution-elasticsearch-llm-challenges</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/entity-resolution-elasticsearch-llm-challenges</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Agora vimos a resolução inteligente de entidades implementada de duas maneiras. Ambas as abordagens começam da mesma forma: preparação e extração de entidades, seguidas pela recuperação de candidatos com Elasticsearch. A partir daí, avaliamos esses candidatos usando um grande modelo de linguagem (LLM), seja por meio de geração de JSON baseada em prompt ou chamada de funções, e exigimos que o modelo forneça uma explicação transparente para seu julgamento.</p><p>Como vimos na <a href="https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-function-calling">postagem anterior</a>, a consistência proporcionada pela chamada de função não é apenas uma mera otimização; é essencial. Uma vez removidos os erros estruturais do ciclo de avaliação, os resultados em cenários padrão (como os do conjunto de dados de nível 4) melhoraram significativamente.</p><p>No entanto, há uma pergunta óbvia a ser respondida:</p><p><em>Essa abordagem ainda funciona quando as coisas realmente ficam confusas?</em></p><p>A resolução de entidades no mundo real raramente falha por causa de casos simples. Ela falha quando nomes cruzam línguas, culturas, sistemas de escrita, períodos de tempo e fronteiras organizacionais. Ela falha quando as pessoas são referenciadas por títulos em vez de nomes, quando as empresas mudam de nome, quando as transliterações não são consistentes e quando o contexto (não a ortografia) é a única coisa que vincula uma menção a uma entidade do mundo real.</p><p>Então, para o post final desta série, colocamos o sistema no que chamamos de <strong>desafio definitivo</strong>.</p><h2>O que faz disso o desafio definitivo?</h2><p>Em avaliações anteriores, testamos o sistema usando conjuntos de dados cada vez mais complexos. Quando chegamos ao nível 4, discutido no post anterior, já estávamos lidando com uma mistura de apelidos, títulos, nomes multilíngues e referências semânticas. Esses testes mostraram que a arquitetura em si era sólida, mas que problemas de confiabilidade, especialmente JSON malformado, estavam prejudicando o recall.</p><p>Com a chamada de função implementada, finalmente tivemos uma base estável. Isso nos deu a oportunidade de fazer uma pergunta mais interessante:</p><p><em>Um único pipeline unificado consegue lidar <strong>com vários tipos diferentes</strong> de problemas de resolução de entidades simultaneamente?</em></p><p>O conjunto de dados de desafio definitivo foi projetado para explorar precisamente essa dimensão.</p><p>Em vez de se concentrar em uma única dificuldade (como apelidos ou transliteração), este conjunto de dados combina <strong>mais de 50 tipos de desafios distintos</strong>, incluindo:</p><ul><li>Convenções culturais de nomeação.</li><li>Referências baseadas em títulos.</li><li>Relações comerciais e mudanças históricas de nome.</li><li>Menções multilíngues e em diferentes sistemas de escrita.</li><li>Desafios complexos que misturam vários dos itens acima.</li></ul><p>O mais importante é que isso não se trata de otimizar para um caso de uso específico. Trata-se de testar se o <em>padrão de design</em> se sustenta quando as regras mudam de entidade para entidade.</p><h2>Visão geral do conjunto de dados</h2><p>O conjunto de dados de desafio definitivo consiste em:</p><ul><li><strong>50 entidades</strong>, abrangendo pessoas, organizações e instituições.</li><li><strong>Cerca de 60 artigos</strong>, com estrutura e complexidade linguística variadas.</li><li><strong>51 categorias distintas de desafios</strong>, agrupadas de forma ampla em:<ul><li>Convenções culturais de nomeação.</li><li>Títulos e o contexto profissional.</li><li>Relacionamentos empresariais e organizacionais.</li><li>Desafios multilíngues e de transliteração.</li><li>Cenários combinados e casos limite.</li></ul></li></ul><p>No início da série, vimos que usar IA generativa (GenAI) para criar conjuntos de dados pode ser uma faca de dois gumes. Sem ele, reunir dados de teste suficientemente grandes e diversos seria extremamente difícil. Mas, se não for controlado, o modelo tende a simplificar demais as coisas.</p><p>Em uma etapa inicial de geração, por exemplo, descobrimos que o modelo incluía frases como &quot;o presidente russo&quot; como apelidos explícitos para Vladimir Putin. Isso pode parecer razoável hoje, mas anula o propósito de testar a resolução contextual. O que acontece se o artigo estiver discutindo a Rússia nos anos 1990? O sistema deve inferir a entidade correta a partir do contexto, não depender de um alias fixo.</p><p>Por esse motivo, este conjunto de dados foi deliberadamente projetado para que <strong>os atalhos não funcionem</strong>. Os pseudônimos não são explicitamente listados quando se espera que o sistema deduza o significado. Frases descritivas não são vinculadas previamente a entidades. As correspondências corretas frequentemente dependem do contexto em nível de artigo, não apenas do texto local.</p><p><strong>Observação importante:</strong> embora demonstremos os recursos do sistema em diversos cenários, este ainda é um protótipo educacional. Os sistemas de produção que lidam com o monitoramento real de entidades sob sanção exigiriam validação adicional, verificações de conformidade, trilhas de auditoria e tratamento especializado para casos de uso sensíveis.</p><h2>Por que esses cenários são difíceis?</h2><p>No primeiro post desta série, apresentamos um exemplo simples, mas ambíguo: &quot;A nova atualização do Swift chegou!&quot; O desafio é que &quot;Swift&quot; pode corresponder a múltiplas entidades do mundo real, dependendo do contexto. Esse exemplo captura uma verdade mais ampla: a linguagem natural é inerentemente ambígua.</p><p>A resolução de entidades, portanto, não é apenas um problema de correspondência de strings. As pessoas normalmente se baseiam normalmente em conhecimento compartilhado, normas culturais e contexto situacional para resolver referências, e raramente percebemos que estamos fazendo isso.</p><p>Considere alguns casos comuns:</p><ul><li>Um título como “o presidente” não tem significado sem contexto geopolítico e temporal.</li><li>O nome de uma empresa pode se referir a uma controladora, uma subsidiária ou uma marca anterior, dependendo de quando o artigo foi escrito.</li><li>O nome de uma pessoa pode aparecer em diferentes ordens, sistemas de escrita ou transliterações, dependendo da língua e da cultura.</li><li>A mesma frase pode se referir legitimamente a diferentes entidades em diferentes contextos, e o sistema deve ser capaz de <em>rejeitar</em> correspondências com a mesma confiança com que as aceita.</li></ul><p>Não existe um conjunto único de regras que lide com tudo isso de forma clara. É por isso que este protótipo separa as responsabilidades de forma tão clara:</p><ul><li>O Elasticsearch reduz o conjunto de candidatos de forma eficiente e transparente.</li><li>O LLM é usado apenas quando o julgamento é necessário e é obrigado a se explicar.</li><li>Recuperação e raciocínio continuam sendo etapas distintas.</li></ul><p>Essa separação se torna ainda mais importante à medida que a diversidade de tipos de desafios aumenta.</p><h2>Como o sistema lida com a diversidade sem exceções específicas</h2><p>Um dos resultados mais interessantes desta avaliação é o que <em>não</em> mudou:</p><ul><li><strong>Não</strong> adicionamos lógica especial para nomes japoneses.</li><li>Não <strong>adicionamos</strong> regras personalizadas para patronímicos árabes.</li><li><strong>Não</strong> adicionamos mapeamentos fixos para nomes históricos de empresas.</li></ul><p>Em vez disso, o sistema se baseou nos mesmos elementos centrais apresentados anteriormente na série:</p><ul><li>Entidades enriquecidas por contexto indexadas para busca semântica.</li><li>Recuperação híbrida (exata, alias e semântica) no Elasticsearch.</li><li>Um pequeno e bem definido conjunto de correspondências candidatas.</li><li>Julgamento de LLM restrito por chamada de função e esquemas mínimos.</li></ul><p>Isso sugere que a flexibilidade do sistema vem da <strong>representação e da arquitetura</strong>, não de uma coleção de regras em constante crescimento.</p><p>Quando o sistema tem sucesso, é porque os candidatos certos são recuperados e o LLM tem contexto suficiente para explicar por que uma referência corresponde (ou não) a uma entidade específica.</p><h2>Resultados: Como foi o desempenho?</h2><p>No conjunto de dados de desafio definitivo, o sistema produziu os seguintes resultados gerais:</p><ul><li><strong>Precisão:</strong> ~91%</li><li><strong>Recall:</strong> ~86%</li><li><strong>Pontuação F1:</strong> ~89%</li><li><strong>Taxa de aceitação em LLM:</strong> ~72%</li></ul><h3>Desempenho em diferentes tipos de desafio</h3><p>A análise dos resultados por tipo de desafio revela pontos fortes e limitações:</p><p><strong>O desempenho mais forte (100% na pontuação F1)</strong> foi observado em áreas como:</p><ul><li>Correspondência de entidades entre sistemas de escrita (cirílico, coreano e chinês).</li><li>Cenários hebraicos (patronímicos, títulos profissionais, títulos religiosos, transliteração).</li><li>Hierarquias de negócios (aeroespacial, manufatura diversificada, corporações multidivisionais).</li><li>Títulos profissionais (acadêmicos, militares, políticos, religiosos).</li><li>Cenários combinados em japonês envolvendo múltiplos sistemas de escrita.</li></ul><p><strong>Forte desempenho (pontuação F1 de 80–99%)</strong> incluiu:</p><ul><li>Figuras políticas internacionais (98%).</li><li>Alterações históricas de nome (90%).</li><li>Hierarquias empresariais complexas (89%).</li><li>Nomes de empresas japonesas (93%).</li><li>Transliteração entre escrituras (86%).</li><li>Patrônimos árabes (86%).</li></ul><p><strong>Áreas mais desafiadoras</strong> incluíram:</p><ul><li>Transliteração avançada (chinês, coreano): 0% de pontuação F1.</li><li>Certos cenários japoneses (honoríficos, ordem dos nomes, variação do sistema de escrita): ~67% F1.</li><li>Alguns cenários árabes (nomes de empresas, referências institucionais): ~40% F1.</li></ul><p>O que é importante aqui é <em>por que</em> o sistema teve dificuldades nesses casos. As falhas não foram causadas por problemas na abordagem geral, mas por limitações em componentes específicos, especialmente o modelo vetorial denso usado para busca semântica em determinados cenários multilíngues.</p><p>Como recuperação e julgamento estão claramente separados, melhorar o desempenho não exige reescrever o sistema. A substituição por um modelo de embeddings multilíngue mais capaz, o enriquecimento do contexto da entidade ou o refinamento das estratégias de recuperação melhoraria os resultados nessas categorias sem alterar a arquitetura central.</p><p>Do ponto de vista arquitetônico, essa é a verdadeira métrica de sucesso.</p><h2>O que isso nos diz sobre o design</h2><p>Olhando para trás na série, alguns padrões se destacam:</p><ul><li><strong>A preparação é mais importante do que a combinação inteligente. </strong>Enriquecer entidades com contexto desde o início reduz drasticamente a ambiguidade depois.</li><li><strong>Os LLMs são mais valiosos como juízes, não como recuperadores. </strong>Pedir <em>que expliquem por que</em> uma combinação faz sentido é muito mais poderoso do que pedir que busquem.</li><li><strong>A confiabilidade possibilita precisão. </strong>A chamada de funções não apenas limpou o JSON; ela revelou o recall que já estava latente na etapa de recuperação.</li><li><strong>A generalização supera a especialização. </strong>Um pequeno número de abstrações bem definidas lidou com dezenas de tipos de desafios sem lógica personalizada.</li></ul><p>Por isso, o protótipo é intencionalmente nativo do Elasticsearch e conservador na forma como utiliza LLMs. O objetivo não é substituir a busca; é tornar a busca explicável em situações onde o significado importa.</p><h2>Conclusão</h2><p>O desafio final não era buscar métricas perfeitas; era sobre responder a uma pergunta mais fundamental:</p><p><em>Uma arquitetura transparente, orientada para busca e assistida por LLM, pode lidar com a ambiguidade de entidades no mundo real sem se limitar a regras ou caixas-pretas?</em></p><p>Para este protótipo educacional, a resposta é sim, com claras ressalvas sobre robustez para produção, conformidade, monitoramento e qualidade dos dados. Se você estiver criando sistemas que precisem justificar <em>por que</em> foi feita uma correspondência de entidade, vale a pena considerar seriamente esse padrão. Espero que esta série tenha mostrado que a resolução de entidades não precisa ser algo misterioso. Com a separação certa das preocupações, torna-se algo sobre o qual você pode refletir, medir e melhorar.</p><p>Este trabalho também sugere um padrão arquitetônico mais amplo. O que surge é uma leve, mas importante, evolução da Retrieval-Augmented Generation (RAG). Em vez de permitir que a recuperação alimente diretamente a geração, introduzimos uma etapa explícita de avaliação. O LLM é usado primeiro para avaliar e verificar a consistência dos candidatos recuperados, e apenas os resultados aprovados podem ampliar a geração. Você pode pensar nisso como Retrieval-Augmented Generation com Avaliação, ou GARAGE, porque quem não gosta de uma boa sigla.</p><p>Quais outros casos de uso poderiam se beneficiar desse padrão? Sistemas que exigem confiança, transparência e raciocínio defensável são candidatos naturais. Trabalhos futuros nessa área devem ser tão interessantes quanto os resultados que vimos aqui, e estou entusiasmado para ver para onde a comunidade vai levar isso a seguir.</p><h2>Próximos passos: Experimente por conta própria</h2><p>Quer ver o desafio definitivo em ação? Confira o <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,05_ultimate_challenge_v3.ipynb,-Initial%20public%20lab"><strong>Notebook do desafio definitivo</strong></a> para ver um passo a passo completo, com implementações reais, explicações detalhadas e exemplos práticos.</p><p>O pipeline completo de resolução de entidades demonstra os conceitos centrais e a arquitetura necessários para uso em produção. Você pode usá-lo como base para construir sistemas que monitorem artigos de notícias, rastreiem menções de entidades e respondam a perguntas sobre quais entidades aparecem em quais artigos, tudo isso mantendo transparência e explicabilidade.<br/></p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/hnsw-elasticsearch-adaptive-early-termination</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/hnsw-elasticsearch-adaptive-early-termination</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tommaso Teofili]]></dc:creator>
            <pubDate>Mon, 02 Mar 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6d3186f609dd56dc5ffe33d70fa9e5cfa05b51f-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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>Um número máximo fixo de nós é visitado.</li><li>Um tempo limite fixo é atingido.</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>&quot;Quantos passos já demos?&quot;</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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a9d07c5153ea64a2426c82487c36846030692bb9-1600x945.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dac56125c4b16d1b596c9876b6ca9ac7b2dc87fa-1600x944.png" style="width: 100%;" /><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>Exploração excessiva para consultas fáceis.</li><li>Encerramento prematuro para consultas complexas.</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&nbsp; $d_{q,i} $ (quantos novos vizinhos foram introduzidos para uma consulta <em>q</em>,na última visita <em>i</em>) junto com a média móvel $\ mu_{q,i}$ e o desvio padrão $\sigma_{q,i}$ dessa taxa de descoberta durante a visita ao gráfico (usando o <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford&#x27;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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1958be7ca4425ade66eaf621ada3533173183598-694x118.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7ee4a55c24853b657df26052b275e8bd76cf0f9-654x156.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2a906997d9a25d74c7038bd9661bc97581e7258e-1600x938.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a114911e232c351dbb814cea20e8b0f1415a717-1600x925.png" style="width: 100%;" /><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>
        </item>
        <item>
            <title><![CDATA[Linguagem de Expressão Comum (CEL): como a entrada CEL melhora a coleta de dados em integrações com o Elastic Agent]]></title>
            <description><![CDATA[Saiba como a Linguagem de Expressão Comum difere de outras linguagens de programação, como a estendemos para a entrada CEL do Filebeat e a flexibilidade que ela oferece para expressar a lógica de coleta de dados nas integrações do Elastic Agent.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/common-expression-language-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/common-expression-language-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Berkhout]]></dc:creator>
            <pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/985c50bfabee3348494eb4307f0b3375a97a0644-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/integrations">As integrações</a> do Elastic Agent permitem que os usuários façam a ingestão de dados para o Elasticsearch a partir de uma ampla variedade de fontes. Eles combinam lógica de coleta, pipelines de ingestão, dashboards e outros artefatos em um pacote que pode ser instalado e gerenciado a partir da interface web do Kibana.</p><p>Integrações configuram <a href="https://www.elastic.co/docs/reference/beats/filebeat/configuration-filebeat-options">entradas do Filebeat</a> para realizar a coleta de dados. Para coletar dados de APIs HTTP, usamos a <a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-httpjson">entrada HTTP JSON</a>. No entanto, mesmo APIs básicas de listagem podem diferir bastante nos detalhes, e o modelo de transformações configuradas em YAML da entrada HTTP JSON pode tornar difícil e, às vezes, impossível expressar a lógica de coleta necessária.</p><p>A <a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">entrada em linguagem de expressão comum (CEL)</a> foi introduzida para permitir uma interação mais flexível com as APIs HTTP. A <a href="https://cel.dev/">CEL</a> é uma linguagem projetada para ser incorporada em aplicações que exigem uma maneira rápida, segura e extensível de expressar condições e transformações de dados. A entrada CEL permite que um desenvolvedor de integrações escreva uma expressão capaz de ler configurações, controlar o próprio estado, fazer solicitações, processar respostas e, por fim, retornar eventos prontos para serem ingeridos.</p><p>Neste artigo, vamos analisar como a CEL difere de outras linguagens de programação, como a estendemos para a entrada CEL, e a flexibilidade e o poder que isso oferece para expressar sua lógica de coleta de dados.</p><h2>CEL e como funciona na entrada</h2><p>A CEL é uma linguagem de expressões. Não possui instruções. Quando você escreve CEL, não indica o que fazer escrevendo instruções, mas sim qual valor produzir ao escrever uma expressão. Cada expressão CEL produz um valor, e expressões menores podem ser combinadas em uma expressão maior para produzir um resultado de acordo com regras mais complexas. Mais adiante, veremos como usar expressões para coisas que podem ser escritas com instruções em outras linguagens.</p><p>CEL é intencionalmente uma linguagem não Turing completa. Não permite loops ilimitados. Posteriormente, veremos como você pode processar listas e mapas usando macros, mas, ao evitar loops ilimitados, a linguagem garante um tempo de execução previsível e limitado para expressões individuais.</p><p>A entrada CEL é configurada com um programa CEL (uma expressão) e algum estado inicial. O estado será fornecido como entrada para o programa. O programa é avaliado para produzir um estado de saída. Se o estado de saída incluir uma lista de eventos, esses serão removidos e publicados. O restante do estado de saída será usado como entrada para a próxima avaliação. Se o estado de saída incluir um ou mais eventos e a bandeira <code>want_more: true</code>, a próxima avaliação será realizada imediatamente; caso contrário, ele irá hibernar pelo restante do intervalo configurado antes de continuar. Veja um diagrama simplificado do fluxo de controle da entrada:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42671541f97e2dba808fd53969fe12f517917f9a-1600x529.png" style="width: 100%;" /><p>A saída de cada avaliação será passada como entrada para a próxima avaliação, enquanto a entrada for executada. Os dados de saída com a chave &quot;<code>cursor</code>&quot; serão mantidos no disco e recarregados após a reinicialização da entrada, mas o restante do estado não será preservado durante as reinicializações.</p><p>A linguagem CEL em si tem funcionalidade limitada e evita efeitos colaterais, mas é extensível. A implementação do <a href="https://github.com/google/cel-go">cel-go</a> acrescenta algumas funcionalidades, como sintaxe e tipos opcionais. A biblioteca <a href="https://github.com/elastic/mito">Mito</a> se baseia no cel-go e adiciona mais funcionalidades, incluindo a capacidade de fazer solicitações HTTP. A entrada da CEL usa a versão da CEL do Mito.</p><h2>Trabalhando com Mito</h2><p>Para criar ou fazer debug de uma integração usando a entrada CEL, o mais importante é entender qual estado de saída o seu programa CEL produzirá para um determinado estado de entrada. Durante o desenvolvimento, pode ser complicado executar o seu programa CEL pela entrada, cercado pela stack completa do Elastic. Uma maneira de ter um ciclo de feedback mais rápido é usar a ferramenta de linha de comando do Mito, que permite executar diretamente um programa CEL e ver a saída que ele produz para uma entrada específica.</p><p>Mito é escrito em Go e pode ser instalado da seguinte forma:</p><pre><code>go install github.com/elastic/mito/cmd/mito@latest</code></pre><p>Quando você executa um programa CEL com o Mito, normalmente fornece a ele dois arquivos: um arquivo JSON com o estado de entrada inicial e outro arquivo com o código-fonte do seu programa CEL:</p><pre><code>mito -data state.json src.cel</code></pre><p>Para facilitar o copiar e colar, os exemplos neste artigo são escritos como comandos únicos que fazem com que o shell crie arquivos temporários em tempo real, agrupando o conteúdo de cada arquivo em <code>&lt;(echo &#x27;...content...&#x27;)</code>. No seu próprio desenvolvimento, trabalhar com arquivos reais será mais fácil.</p><h2>Busca de dados de problemas do GitHub</h2><p>O exemplo a seguir inclui um programa CEL completo que buscará dados sobre problemas na <a href="https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues">API do GitHub</a>. Seu estado de entrada inicial tem uma URL para o endpoint da API e algumas informações sobre como se deve lidar com a paginação. O programa CEL usa os dados no estado de entrada para gerar uma solicitação. Ele decodificará a resposta, produzirá eventos a partir dela e os retornará como parte de seu estado de saída.</p><pre><code>mito -data <(echo '
  {
    "url": "https://api.github.com/repos/elastic/integrations/issues",
    "per_page": 3,
    "max_pages": 3
  }
') <(echo '
  int(state.?cursor.page.orValue(1)).as(page,
    (
      state.url + "?" + {
        "state": ["all"],
        "sort": ["created"],
        "direction": ["asc"],
        "per_page": [string(state.per_page)],
        "page": [string(page)],
      }.format_query()
    ).as(full_url,
      request("GET", full_url).with({
        "Header": {
          "Accept": ["application/vnd.github+json"],
          "X-GitHub-Api-Version": ["2022-11-28"],
        }
      }).do_request().as(resp,
        resp.Body.decode_json().as(data,
          state.with({
            "events": data.map(i, {
              "html_url": i.html_url,
              "title": i.title,
              "created_at": i.created_at,
            }),
            "cursor": { "page": page + 1 },
            "want_more": size(data) == state.per_page && page < state.max_pages,
          })
        )
      )
    )
  )
')</code></pre><p>Sua primeira avaliação produz a seguinte saída:</p><pre><code>{
  "cursor": {
    "page": 2
  },
  "events": [
    {
      "created_at": "2018-09-14T09:47:35Z",
      "html_url": "https://github.com/elastic/integrations/issues/3250",
      "title": "Increase support of log formats in haproxy filebeat module"
    },
    {
      "created_at": "2019-02-06T12:37:37Z",
      "html_url": "https://github.com/elastic/integrations/issues/487",
      "title": "ETCD Metricbeat module needs polishing and grooming"
    },
    {
      "created_at": "2019-08-13T11:33:11Z",
      "html_url": "https://github.com/elastic/integrations/pull/1",
      "title": "Initial structure"
    }
  ],
  "max_pages": 3,
  "per_page": 3,
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "want_more": true
}</code></pre><p>Os eventos serão removidos e, quando executados na entrada CEL, serão publicados para ingestão. O restante da saída será fornecido para a próxima avaliação do programa CEL como seu estado de entrada.</p><p></p><p>Para entender como esse programa CEL funciona, vamos analisar alguns exemplos menores de CEL e discutir mais detalhes sobre como a entrada CEL funciona.</p><h2>Noções básicas do CEL</h2><p>Na linguagem CEL, não há declarações; só existem expressões. Cada expressão CEL bem-sucedida é avaliada para um valor final. Aqui está uma das menores expressões de CEL que você pode escrever, junto com sua saída:</p><pre><code>mito <(echo '
  "hello" + " " + "world"
')</code></pre><pre><code>"hello world"</code></pre><p>Muitas expressões simples são intuitivas. Operações matemáticas são suportadas apenas em valores do mesmo tipo (por exemplo, <code>int</code> com <code>int</code>), então converta os tipos conforme necessário (aqui de <code>int</code> para <code>double</code>):</p><pre><code>mito <(echo '
  double((1 + 2) * (3 + 4)) / 2.0
')</code></pre><pre><code>10.5</code></pre><p>Não há variáveis na linguagem CEL, mas uma expressão pode receber um nome e ser usada em uma expressão maior com a ajuda da macro <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-As__Macro_-Collections"><code>as</code></a> de Mito. Neste exemplo, a expressão <code>(1 + 1)</code> avalia para o valor <code>2</code>, e <code>.as(n, ...)</code> dá a esse valor o nome <code>n</code> para uso na expressão <code>&quot;one plus one is &quot;+string(n)</code>:</p><pre><code>mito <(echo '
  (1 + 1).as(n, "one plus one is "+string(n))
')</code></pre><pre><code>"one plus one is 2"</code></pre><p>Também é possível acumular informações em um mapa e utilizá-las posteriormente na expressão, como demonstrado aqui usando <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-With-Collections"><code>with</code></a>:</p><pre><code>mito <(echo '
  { "key": "value" }.with({ "key2": "value2" }).as(data,
    {
      "data": data,
      "size": size(data),
    }
  )
')</code></pre><pre><code>{
  "data": {
    "key": "value",
    "key2": "value2"
  },
  "size": 2
}</code></pre><p>Observe esse exemplo novamente. Note que a parte aninhada, <code>({ &quot;data&quot;: data, &quot;size&quot;: size(data), })</code>, nos dá a forma do valor final. É um mapa com as chaves <code>&quot;data&quot;</code> e <code>&quot;size&quot;</code>. Os valores dessas chaves dependem de <code>data</code>, que é definido pela parte externa da expressão. Ler as expressões da CEL de dentro para fora pode ajudar a ver o que elas vão retornar.</p><p>A CEL não possui instruções de fluxo de controle, como <code>if</code>, mas a ramificação condicional pode ser feita com o operador ternário:</p><pre><code>mito <(echo '
  1 + 1 < 12 ? "few" : "many"
')</code></pre><pre><code>"few"</code></pre><p>Ciclos ilimitados e recursão não são compatíveis, pois a CEL não é uma linguagem de Turing completa. Isso torna o tempo de execução previsível e proporcional ao tamanho dos dados de entrada e à complexidade da expressão.</p><p>Embora ciclos ilimitados não sejam possíveis em expressões CEL individuais, você pode processar listas e mapas usando macros como <a href="https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros"><code>map</code></a>:</p><pre><code>mito <(echo '
  [1, 2, 3].map(x, x * 2)
')</code></pre><pre><code>[2, 4, 6]</code></pre><p>Nesta seção, abordamos:</p><ul><li>Strings, números, listas e mapas.</li><li>Concatenação de strings.</li><li>Operações matemáticas.</li><li>Conversão de tipo.</li><li>Condicionais.</li><li>Nomeando subexpressões.</li><li>Processando coleções.</li></ul><p>Em seguida, veremos como fazer solicitações HTTP.</p><h2>Requisições</h2><p>O Mito estende a CEL com a capacidade de fazer <a href="https://pkg.go.dev/github.com/elastic/mito/lib#HTTP">requisições HTTP</a>:</p><pre><code>mito <(echo '
  get("https://example.com").as(resp, string(resp.Body))
')</code></pre><pre><code>"<!doctype html><html lang=\"en\"><head><title>Example Domain</title>..."</code></pre><p>As requisições podem ser construídas explicitamente antes de serem executadas. Isso possibilita o uso de diferentes métodos HTTP e a adição de cabeçalhos e corpo da requisição.</p><p>Neste exemplo, criamos uma URL com a ajuda de <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Format_Query-HTTP"><code>format_query</code></a>, adicionamos um cabeçalho à solicitação e analisamos o corpo da resposta com <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Decode_JSON-JSON"><code>decode_json</code></a>. Quando você receber a opção <code>-log_requests</code>, o Mito loga informações detalhadas no formato JSON sobre cada requisição e resposta.</p><pre><code>mito -log_requests <(echo '
  request("GET",
    "https://postman-echo.com/get?" + {
        "q": ["query value"]
     }.format_query()
  ).with({
    "Header": { "Accept": ["application/json"] }
  }).do_request().as(resp, {
    "status": resp.StatusCode,
    "data": resp.Body.decode_json(),
  })
')</code></pre><pre><code>{"time":"...","level":"INFO","msg":"HTTP request",...}
{"time":"...","level":"INFO","msg":"HTTP response",...}
{
  "data": {
    "args": {
      "q": "query value"
    },
    "headers": {
      "accept": "application/json",
      "accept-encoding": "gzip, br",
      "host": "postman-echo.com",
      "user-agent": "Go-http-client/2.0",
      "x-forwarded-proto": "https"
    },
    "url": "https://postman-echo.com/get?q=query+value"
  },
  "status": 200
}</code></pre><h2>Gestão do estado e avaliações</h2><p>Agora que abordamos como fazer requisições e os fundamentos da CEL necessários para produzir o estado de saída desejado, vamos dar uma olhada mais detalhada no que devemos colocar no estado de saída e como isso nos permite direcionar o processamento posterior.</p><p>O programa CEL de uma integração precisa garantir que o estado de saída seja adequado para uso como entrada na próxima avaliação. A configuração define o estado inicial, que deve ser repetido na saída com quaisquer mudanças apropriadas. Uma maneira fácil de fazer isso é usar <code>state.with({ ... })</code>, para repetir o mapa de estados com algumas sobrescrições. Um padrão comum para programas pequenos é envolver o programa inteiro em <code>state.with()</code>, para que a propagação de estados não precise ser repetida em cada ramo que gera dados de saída (por exemplo, sucesso, erros).</p><p>Quando há valores de estado inicializados por uma avaliação em vez de codificados fixamente no estado inicial de entrada, o programa precisará verificar um valor existente antes de definir o inicial. Isso é algo em que o suporte para <a href="https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes">sintaxe e tipos opcionais</a> pode ajudar. Ao usar um ponto de interrogação antes do nome do campo em uma chave de mapa, o acesso torna-se opcional: pode ou não resolver para um valor, mas acessos opcionais adicionais são possíveis e é fácil fornecer um padrão se não houver valor presente:<br/></p><pre><code>mito -data <(echo '{}') <(echo '
  int(state.?counter.orValue(0)).as(counter,
    state.with({
      "counter": counter + 1,
      "want_more": counter + 1 < 3,
    })
  )
')</code></pre><pre><code>{ "counter": 1, "want_more": true }
{ "counter": 2, "want_more": true }
{ "counter": 3, "want_more": false }</code></pre><p>Nesse exemplo, o valor contador lido do estado é convertido para <code>int</code> porque todos os números são serializados no estado como números de ponto flutuante, de acordo com as convenções estabelecidas pelo tipo de <code>Number</code> do JSON e JavaScript. Também deve ser notado que <code>&quot;want_more&quot;: true</code> é respeitado aqui pelo Mito, mas quando executado na entrada CEL, a avaliação só será repetida se a saída também contiver eventos.</p><p>É uma exigência dos programas CEL executados pela entrada CEL que retornem uma chave <code>&quot;events&quot;</code> no mapa de saída. O valor pode ser uma lista de mapas de eventos, uma lista vazia ou um único mapa de eventos. O caso de evento único geralmente é usado para erros. O evento será publicado pela entrada, mas seu valor também será log, e se definir um valor <code>error.message</code> , ele será usado para atualizar o status de saúde da Fleet da integração. Se seu programa gerar um único evento que não seja erro, é melhor agrupá-lo em uma lista.</p><p>Confira novamente a saída anterior do nosso programa de problemas do GitHub:</p><pre><code>{
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "per_page": 3,
  "max_pages": 3,
  "cursor": {
    "page": 2
  },
  "events": [
    { ... },
    { ... },
    { ... }
  ],
  "want_more": true
}</code></pre><p>O programa gerenciou efetivamente o estado por:</p><ul><li>Repetindo valores de estado inicial em <code>url</code>, <code>per_page</code> e <code>max_pages</code>.</li><li>Adicionando estados que devem ser mantidos durante reinícios em <code>cursor.page</code>.</li><li>Eventos de retorno prontos para serem publicados na lista <code>events</code>.</li><li>Solicitar reavaliação imediata com <code>want_more: true</code>.</li></ul><p>Agora que você entende o acesso opcional e o gerenciamento de estado, bem como os conceitos básicos da CEL e as solicitações HTTP, o programa completo de problemas do GitHub deve estar legível. Tente executar o programa com o Mito e experimente algumas alterações.</p><h2>Revisão e recursos</h2><p>Neste artigo, analisamos o que é a CEL e como ela foi estendida na biblioteca Mito para uso na entrada CEL. Observamos a flexibilidade da CEL em um programa de exemplo que busca informações de problemas na API do GitHub e analisamos todos os detalhes necessários para entender esse programa, abrangendo o acesso às configurações no estado inicial, a interação com APIs HTTP, o retorno de eventos a serem ingeridos e o gerenciamento do estado para execuções posteriores do programa.</p><p>Para aprender mais e construir integrações usando a entrada da CEL, há vários recursos que valem a pena explorar:</p><ul><li><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">Entrada CEL - Documentação do Filebeat</a></li><li><a href="https://pkg.go.dev/github.com/elastic/mito">Documentação Mito</a></li><li><a href="https://cel.dev/">Linguagem de expressão comum - website cel.dev</a></li><li><a href="https://www.elastic.co/docs/extend/integrations">Crie uma Integração - Documentação Elastic</a></li></ul><p>E talvez o recurso mais valioso para criar integrações com a entrada CEL seja o código CEL das integrações existentes do Elastic, que pode ser encontrado no GitHub:</p><p><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"><code>cel.yml.hbs</code> arquivos no repositório de integrações Elastic - GitHub</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Resolução de entidades com Elasticsearch & LLMs, Parte 2: Correspondência de entidades com julgamento LLM e busca semântica]]></title>
            <description><![CDATA[Uso de busca semântica e julgamento transparente de LLM para a resolução de entidades no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-entity-resolution-llm-semantic-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-entity-resolution-llm-semantic-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/473ca4357c7d60f690edbd2a844acda169aca9c3-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Na<a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch"> Parte 1</a>, preparamos nossa lista de monitoramento e extraímos as menções às entidades. Agora, estamos prontos para responder à pergunta difícil: a qual entidade uma menção realmente se refere? Vamos voltar ao exemplo do primeiro blog desta série, que explica por que precisamos de resolução de entidades: &quot;A atualização Swift chegou!&quot; Imagine que esta manchete vem acompanhada de um pouco mais de contexto:</p><ol><li>A nova atualização do Swift chegou! Os desenvolvedores estão ansiosos para experimentar os novos recursos.</li><li>A nova atualização do Swift chegou! O novo álbum será lançado no próximo mês.</li></ol><p>Com esse contexto adicional, devemos conseguir resolver o nome &quot;Swift&quot; para a entidade correta.</p><p>Na <a href="https://www.elastic.co/search-labs/blog/entity-resolution-llm-elasticsearch">postagem anterior</a>, configuramos nossa lista de observação e enriquecemos as entidades com contexto adicional. Olhando nossos exemplos acima, precisamos ter pelo menos as seguintes duas entidades na lista: Taylor Swift e Swift Programming Language. Também abordamos como extraímos menções a entidades do texto. Ambos os exemplos extrairiam &quot;Swift&quot;. Com esses ingredientes prontos, a lista de observação enriquecida e as entidades extraídas, finalmente estamos prontos para apresentar a estrela do show: a correspondência de entidades.</p><p><strong>Lembre-se:</strong> este é um protótipo educacional projetado para ensinar conceitos de correspondência de entidades. Os sistemas de produção podem usar diferentes modelos de linguagem grande (LLMs), regras de correspondência personalizadas, pipelines de julgamento especializados ou abordagens de conjunto que combinam várias estratégias de correspondência.</p><h2>O problema: por que a correspondência é difícil</h2><p>A linguagem humana é algo extraordinário. Uma das propriedades mais interessantes é sua criatividade infinita. Podemos gerar e entender um número infinito de novas frases. Será que é de se estranhar, então, que correspondências exatas na resolução das entidades sejam raras? Autores se esforçam para ser criativos quando podem. Ficaria bastante cansativo se tivéssemos que escrever e ler nomes completos sempre que uma entidade fosse mencionada. Portanto, embora as correspondências exatas sejam fáceis, a realidade é que precisamos de uma abordagem mais sofisticada para a resolução de entidades: uma que seja robusta o suficiente para lidar com pelo menos parte da criatividade ilimitada de autores humanos. Por isso, dividimos o problema em duas etapas: usar o Elasticsearch para recuperar candidatos plausíveis em larga escala, e depois usar um LLM para julgar se esses candidatos realmente se referem à mesma entidade do mundo real.</p><h2>A solução: correspondência em três etapas com julgamento transparente do LLM</h2><p>Estamos no meio de uma mudança de paradigma na forma como usamos computadores. Assim como a ascensão da Internet nos levou da computação localizada para uma rede conectada globalmente, a IA generativa (GenAI) está mudando fundamentalmente a forma como o conteúdo, o código e as informações são criados. Na verdade, o protótipo educacional que acompanha essa série foi quase exclusivamente &quot;codificado por vibração&quot; usando um LLM, com orientação cuidadosa do autor. Isso não quer dizer que os LLMs tenham ou que alcançarão o tipo de produtividade inerente à linguagem humana, mas significa que agora temos um recurso poderoso para ajudar na resolução de entidades.</p><p>Um padrão comum que usamos com GenAI é a retrieval augmented generation (RAG). Aqui, <em>retrieval</em> significa recuperar entidades candidatas (não gerar respostas), e o LLM é usado estritamente para avaliação e explicação da correspondência. Embora <em>pudéssemos</em> pedir a um LLM para nos ajudar com a resolução de entidades de ponta a ponta, essa abordagem é dispendiosa, tanto em termos de tempo quanto de dinheiro. A RAG ajuda os LLMs a realizar seu trabalho usando maneiras mais eficientes de fornecer contexto ao LLM, capacitando-o a auxiliar de forma eficiente na resolução de entidades.</p><p>Para a parte de recuperação do RAG, voltamos novamente ao Elasticsearch. Primeiro, encontramos possíveis correspondências usando uma combinação de correspondência exata, correspondência com aliases e busca híbrida, que combina busca semântica e por palavra-chave. Assim que encontramos essas possíveis correspondências, as enviamos para um LLM para julgamento. O LLM atua como avaliador final de correspondência. Também fazemos o LLM explicar seu raciocínio, um diferenciador importante em relação a outros sistemas de resolução de entidades. Sem essas explicações, a resolução de entidades é uma caixa preta; com elas, podemos ver por nós mesmos por que uma correspondência faz sentido.</p><h2>Conceitos-chave: correspondência em três etapas, busca híbrida e julgamento transparente de LLM</h2><p><strong>O que é a correspondência em três etapas?</strong> No início deste projeto, hipotetizamos que a busca semântica será uma parte crucial do sistema, mas nem toda correspondência exige uma busca tão sofisticada. Para encontrar correspondências de forma eficiente, adotamos uma abordagem progressiva ao problema. Primeiro, verificamos correspondências exatas usando busca por palavras-chave. Se encontrarmos essa correspondência, nosso trabalho estará feito e poderemos seguir em frente. Se a correspondência exata falhar, recorremos à correspondência de alias. No protótipo, a correspondência de alias também é feita usando correspondência exata com palavras-chave, para simplificar. Na produção, você pode expandir essa etapa com normalização, regras de transliteração, correspondência fuzzy ou tabelas de alias curadas. Se ainda não encontramos uma possível correspondência nas duas primeiras etapas, é hora de introduzir a busca semântica por meio da busca híbrida do Elasticsearch com fusão recíproca de classificação (RRF).</p><p><strong>O que é busca híbrida?</strong> No Elasticsearch, podemos usar a busca semântica para encontrar correspondências significativas que levem em conta o contexto. O Elasticsearch é amplamente utilizado para busca vetorial e recuperação híbrida. A semelhança semântica é poderosa para o significado, mas não substitui a filtragem estruturada (por exemplo, por intervalos de tempo, locais ou identificadores) e geralmente é desnecessária quando uma correspondência exata está disponível. O Elasticsearch se destacou com a busca lexical, que é ótima em tarefas onde a busca semântica não se encaixa. Para aproveitar ao máximo ambas as abordagens, usamos a busca lexical junto com a busca semântica em uma única consulta híbrida. Depois, juntamos os resultados para encontrar as correspondências mais prováveis usando o RRF. No protótipo, os dois melhores resultados tornam-se correspondências potenciais que podem ser enviadas para avaliação do LLM.</p><p><strong>Por que julgamento de LLM?</strong> Julgamentos e explicações de LLM permitem que nosso sistema trate ambiguidade e contexto de forma transparente. Isso é vital para casos como &quot;o presidente&quot;, que pode se referir a múltiplas entidades, dependendo do contexto, mas também faz com que apelidos e variações culturais funcionem bem no sistema. Finalmente, quando consideramos tarefas de missão crítica, como identificar entidades a partir de listas de sanções, precisamos saber por que uma combinação foi aceita para confiar no sistema. Crucialmente, o LLM não busca o corpus completo; ele avalia apenas o pequeno conjunto de candidatos retornados pelo Elasticsearch.</p><h2>Resultados do mundo real: correspondência com raciocínio de LLM</h2><p>Um dos principais desafios de qualquer tarefa de processamento de linguagem natural é a criação de um documento de referência, um &quot;gabarito&quot; que nos diga quais são os resultados esperados. Sem isso, é praticamente impossível avaliar o desempenho de um sistema em uma tarefa, mas criar um documento desse tipo pode ser um processo trabalhoso. Para o protótipo de resolução de entidades, recorremos novamente à GenAI para nos ajudar a configurar os dados que pudéssemos usar para os testes.</p><p>Primeiro, definimos vários tipos de desafios, como apelidos e transliteração, e então pedimos ao LLM para criar uma coleção em camadas de conjuntos de dados que se tornariam progressivamente maiores e mais desafiadores para o sistema. A criação dos conjuntos de dados foi menos simples do que se esperava. O LLM tinha uma forte propensão para &quot;trapacear&quot; ao tornar muito fácil obter a resposta certa. Por exemplo, um dos tipos de desafio focou no contexto semântico. Este tipo incluiu coisas como resolver &quot;autor russo&quot; para &quot;Liev Tolstói&quot;. O LLM incorretamente colocou &quot;autor russo&quot; como um alias para &quot;Leo Tolstoy&quot;, o que negou a necessidade de uma busca híbrida para encontrar a correspondência.</p><p>Após várias refatorações para corrigir problemas como esse, tínhamos cinco níveis de conjunto de dados para trabalhar. Os níveis 1 a 4 eram progressivamente maiores, com mais tipos de desafio. O Tier 5 era o conjunto de dados do &quot;desafio supremo&quot;, composto pelos exemplos mais difíceis de todos os tipos de desafio. Todos os dados dos testes estão disponíveis no <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/comprehensive_evaluation">diretório de avaliação completo</a>.</p><p>Para avaliar nossa abordagem de resolução de entidades baseada em prompts, focamos nossa atenção no conjunto de dados de nível 4. Um ponto importante é que a avaliação foi realizada como um experimento controlado para que pudéssemos focar na qualidade da correspondência de entidades. Os dados da lista de observação foram pré-enriquecidos com contexto, e as entidades foram extraídas do artigo antecipadamente. Isso garantiu que a avaliação fosse focada em correspondência, e não na precisão da extração. Isso isola a qualidade da correspondência; o desempenho de ponta a ponta também dependeria da qualidade do recall e do enriquecimento da extração.</p><h3>Conjunto de dados de avaliação</h3><p>O conjunto de dados de avaliação de nível 4 fornece um teste abrangente das capacidades do sistema:[1]</p><ul><li><strong>Entidades da lista de observação:</strong> 66 entidades de diversos tipos (pessoas, organizações, locais).</li><li><strong>Artigos de teste:</strong> 69 artigos que abrangem cenários reais de resolução de entidades.</li><li><strong>Correspondências esperadas:</strong> 206 correspondências de entidades esperadas em todos os artigos.</li><li><strong>Tipos de desafio: </strong>15 tipos diferentes de desafio que testam vários aspectos da resolução de entidades.</li></ul><p>Os tipos de desafios incluídos no conjunto de dados são:</p><ul><li><strong>Apelidos:</strong> &quot;Bob Smith&quot; → &quot;Robert Smith&quot; (sete artigos).</li><li><strong>Títulos e honoríficos:</strong> &quot;Dr. Sarah Williams&quot; → &quot;Sarah Williams&quot; (cinco artigos).</li><li><strong>Contexto semântico:</strong> &quot;autor russo&quot; → &quot;Liev Tolstói&quot; (oito artigos).</li><li><strong>Nomes multilíngues:</strong> manuseio de nomes em diferentes scripts (seis artigos).</li><li><strong>Entidades empresariais:</strong> variações de nome corporativo (sete artigos).</li><li><strong>Referências executivas: </strong>&quot;CEO da Microsoft&quot; → &quot;Satya Nadella&quot; (cinco artigos).</li><li><strong>Líderes políticos:</strong> referências baseadas em títulos (cinco artigos).</li><li><strong>Iniciais:</strong> &quot;J. Smith&quot; → &quot;John Smith&quot; (três artigos).</li><li><strong>Variações na ordem dos nomes:</strong> diferentes convenções de ordenação de nomes (três artigos).</li><li><strong>Nomes truncados:</strong> correspondências parciais de nomes (três artigos).</li><li><strong>Divisão de nomes:</strong> nomes divididos no texto (três artigos).</li><li><strong>Falta de espaços/hífens:</strong> variações de formatação (dois artigos).</li><li><strong>Transliteração:</strong> correspondência de nomes entre escrituras (dois artigos).</li><li><strong>Desafios combinados:</strong> Vários desafios em um único artigo (seis artigos).</li><li><strong>Negócios complexos:</strong> relações comerciais hierárquicas (cinco artigos).</li></ul><p>Vamos ver como a resolução de entidades baseada em prompts foi realizada.</p><h3>Desempenho geral</h3><p>Os resultados mostram que a avaliação de correspondência baseada no LLM é muito promissora, mas também revelam um problema significativo de confiabilidade. Como cada par de candidatos deve ser avaliado pelo LLM, falhas na saída estruturada podem suprimir a aceitação e a recuperação, mesmo quando a recuperação está funcionando bem.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Métrica</td><td style="border: 1px solid #ccc; padding: 4px;">Valor</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Precisão</td><td style="border: 1px solid #ccc; padding: 4px;">83,8%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Recall</td><td style="border: 1px solid #ccc; padding: 4px;">62,6%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Pontuação F1</td><td style="border: 1px solid #ccc; padding: 4px;">71,7%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Total de correspondências encontradas</td><td style="border: 1px solid #ccc; padding: 4px;">344</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Taxa de aceitação do LLM</td><td style="border: 1px solid #ccc; padding: 4px;">44,8%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Taxa de erro</td><td style="border: 1px solid #ccc; padding: 4px;">30,2%</td>
                    </tr>
              </tbody>
            </table>
          <h3>O problema da taxa de erro</h3><p>Lembre-se de que o primeiro passo que damos no protótipo é criar potenciais pares de correspondência usando o Elasticsearch. Cada uma dessas possíveis correspondências precisa ser avaliada pelo LLM. Para processar eficientemente todas essas correspondências, agrupamos as chamadas de LLM em lote. Isso reduz os custos da API e a latência, mas também há um risco aumentado de obter JSON malformado na saída. À medida que o tamanho do lote aumenta, o JSON se torna mais longo e complexo, tornando mais provável que o LLM gere JSON inválido. É daí que decorre a taxa de erro de 30%. Na avaliação, usamos um tamanho de lote de cinco correspondências por solicitação. Mesmo com este tamanho de lote conservador, ainda vemos falhas na análise JSON, o que distorce significativamente os resultados da avaliação.</p><h2>O que vem a seguir: otimização da integração com LLMs</h2><p>Agora que combinamos entidades usando busca semântica e julgamento de LLM, temos um pipeline completo de resolução de entidades. Essa abordagem introduz um novo modo de falha, no entanto, quando o julgamento do modelo está correto, mas sua saída não é utilizável. Podemos otimizar a integração do LLM para maior confiabilidade e eficiência de custos. No próximo post, exploraremos como usar o chamado de função para saída estruturada, que garante estrutura e segurança de tipos, ao mesmo tempo em que reduz erros e custos.</p><h2>Experimente você mesmo</h2><p>Quer ver a correspondência de entidades em ação? Confira o <a href="https://github.com/jesslm/entity-resolution-lab-public/tree/main/notebooks#:~:text=5%20minutes%20ago-,03_entity_matching_v3.ipynb,-Initial%20public%20lab">notebook do Entity Matching</a> para ver um passo a passo completo com implementações reais, explicações detalhadas e exemplos práticos. O caderno mostra exatamente como combinar entidades usando busca em três etapas, busca híbrida com RRF e julgamento baseado em LLM com raciocínio.</p><p><strong>Lembre-se:</strong> este é um protótipo educacional projetado para ensinar os conceitos. Ao construir sistemas de produção, considere fatores adicionais, como seleção de modelos, otimização de custos, requisitos de latência, validação de qualidade, tratamento de erros e monitoramento, que não são abordados neste protótipo focado em aprendizado.</p><h2>Notas</h2><ol><li>Esses conjuntos de dados são sintéticos e projetados para educação; eles se aproximam de desafios reais, mas não representam nenhum domínio de produção específico.</li></ol>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/opensearch-vs-elasticsearch-filtered-vector-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/opensearch-vs-elasticsearch-filtered-vector-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
            <pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fb7ee623925ca6696d643e437ce8efe5fe749079-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5a52a07773f0942c2baa732dacfe782aac0f415-1600x683.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/54ac476ff20a3cf93484298c9ae47612c12fc110-800x417.png" style="width: 100%;" /><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>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</li><li>Consulta vetorial: “Mochila de bordo resistente à água para notebook de 15 polegadas”</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>&quot;Mochila de viagem com compartimento para laptop&quot;</li><li>&quot;Mochila urbana resistente à água 15 polegadas&quot;</li><li>&quot;Mochila leve para cabine&quot;</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>Texto sobre materiais e resistência à água</li><li>Dimensões e ajuste do compartimento para laptop</li><li>Política de devolução ou restrições de garantia</li><li>Opções alternativas se o estoque estiver baixo</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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c50d1df00968cac18149a2799e6242fbe49b66a0-1600x990.png" style="width: 100%;" /><h4>Alguns insights importantes</h4><ul><li><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</li><li>Recall: Recall@100 medido para essa configuração</li><li>Latência média (ms): latência média de ponta a ponta por consulta</li><li>Taxa de transferência: consultas por segundo</li><li>Recall (%): elevação relativa do recall do Elasticsearch x OpenSearch (Elasticsearch menos OpenSearch)/OpenSearch</li><li>Latência Xs: a latência média do OpenSearch dividida pela latência média do Elasticsearch</li><li>Throughput Xs: Throughput do Elasticsearch dividido pelo throughput do OpenSearch</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Mecanismo</td><td style="border: 1px solid #ccc; padding: 4px;">`s_n_r_value`</td><td style="border: 1px solid #ccc; padding: 4px;">Recall</td><td style="border: 1px solid #ccc; padding: 4px;">Latência Média (ms)</td><td style="border: 1px solid #ccc; padding: 4px;">Taxa de transferência</td><td style="border: 1px solid #ccc; padding: 4px;">Recall %</td><td style="border: 1px solid #ccc; padding: 4px;">Latência Xs</td><td style="border: 1px solid #ccc; padding: 4px;">Taxa de transferência Xs</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_250_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,7704</td><td style="border: 1px solid #ccc; padding: 4px;">25</td><td style="border: 1px solid #ccc; padding: 4px;">534,75</td><td style="border: 1px solid #ccc; padding: 4px;">9,70%</td><td style="border: 1px solid #ccc; padding: 4px;">2,28</td><td style="border: 1px solid #ccc; padding: 4px;">1,91</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_250_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,7023</td><td style="border: 1px solid #ccc; padding: 4px;">57,08</td><td style="border: 1px solid #ccc; padding: 4px;">279,58</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8577</td><td style="border: 1px solid #ccc; padding: 4px;">25,42</td><td style="border: 1px solid #ccc; padding: 4px;">524,14</td><td style="border: 1px solid #ccc; padding: 4px;">7,20%</td><td style="border: 1px solid #ccc; padding: 4px;">2,4</td><td style="border: 1px solid #ccc; padding: 4px;">2</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8001</td><td style="border: 1px solid #ccc; padding: 4px;">60,9</td><td style="border: 1px solid #ccc; padding: 4px;">262,12</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_750_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8947</td><td style="border: 1px solid #ccc; padding: 4px;">29,67</td><td style="border: 1px solid #ccc; padding: 4px;">528,09</td><td style="border: 1px solid #ccc; padding: 4px;">5,72%</td><td style="border: 1px solid #ccc; padding: 4px;">2,25</td><td style="border: 1px solid #ccc; padding: 4px;">2,21</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_750_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8463</td><td style="border: 1px solid #ccc; padding: 4px;">66,76</td><td style="border: 1px solid #ccc; padding: 4px;">239,11</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9156</td><td style="border: 1px solid #ccc; padding: 4px;">29,65</td><td style="border: 1px solid #ccc; padding: 4px;">534,5</td><td style="border: 1px solid #ccc; padding: 4px;">4,66%</td><td style="border: 1px solid #ccc; padding: 4px;">2,46</td><td style="border: 1px solid #ccc; padding: 4px;">2,44</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,8748</td><td style="border: 1px solid #ccc; padding: 4px;">72,88</td><td style="border: 1px solid #ccc; padding: 4px;">219,01</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9386</td><td style="border: 1px solid #ccc; padding: 4px;">31,84</td><td style="border: 1px solid #ccc; padding: 4px;">497,3</td><td style="border: 1px solid #ccc; padding: 4px;">3,38%</td><td style="border: 1px solid #ccc; padding: 4px;">2,71</td><td style="border: 1px solid #ccc; padding: 4px;">2,68</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_1500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9079</td><td style="border: 1px solid #ccc; padding: 4px;">86,16</td><td style="border: 1px solid #ccc; padding: 4px;">185,4</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9507</td><td style="border: 1px solid #ccc; padding: 4px;">34,69</td><td style="border: 1px solid #ccc; padding: 4px;">457,2</td><td style="border: 1px solid #ccc; padding: 4px;">2,57%</td><td style="border: 1px solid #ccc; padding: 4px;">2,98</td><td style="border: 1px solid #ccc; padding: 4px;">2,96</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9269</td><td style="border: 1px solid #ccc; padding: 4px;">103,36</td><td style="border: 1px solid #ccc; padding: 4px;">154,55</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9582</td><td style="border: 1px solid #ccc; padding: 4px;">37,9</td><td style="border: 1px solid #ccc; padding: 4px;">418,43</td><td style="border: 1px solid #ccc; padding: 4px;">1,99%</td><td style="border: 1px solid #ccc; padding: 4px;">3,28</td><td style="border: 1px solid #ccc; padding: 4px;">3,26</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_2500_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9395</td><td style="border: 1px solid #ccc; padding: 4px;">124,29</td><td style="border: 1px solid #ccc; padding: 4px;">128,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_3000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9636</td><td style="border: 1px solid #ccc; padding: 4px;">41,86</td><td style="border: 1px solid #ccc; padding: 4px;">379,4</td><td style="border: 1px solid #ccc; padding: 4px;">1,62%</td><td style="border: 1px solid #ccc; padding: 4px;">3,46</td><td style="border: 1px solid #ccc; padding: 4px;">3,44</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_3000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9482</td><td style="border: 1px solid #ccc; padding: 4px;">144,67</td><td style="border: 1px solid #ccc; padding: 4px;">110,34</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_4000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9705</td><td style="border: 1px solid #ccc; padding: 4px;">50,28</td><td style="border: 1px solid #ccc; padding: 4px;">316,21</td><td style="border: 1px solid #ccc; padding: 4px;">1,06%</td><td style="border: 1px solid #ccc; padding: 4px;">3,87</td><td style="border: 1px solid #ccc; padding: 4px;">3,85</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_4000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9603</td><td style="border: 1px solid #ccc; padding: 4px;">194,36</td><td style="border: 1px solid #ccc; padding: 4px;">82,22</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_5000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9749</td><td style="border: 1px solid #ccc; padding: 4px;">58,77</td><td style="border: 1px solid #ccc; padding: 4px;">270,91</td><td style="border: 1px solid #ccc; padding: 4px;">0,73%</td><td style="border: 1px solid #ccc; padding: 4px;">4,43</td><td style="border: 1px solid #ccc; padding: 4px;">4,41</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_5000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9678</td><td style="border: 1px solid #ccc; padding: 4px;">260,33</td><td style="border: 1px solid #ccc; padding: 4px;">61,38</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_6000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9781</td><td style="border: 1px solid #ccc; padding: 4px;">66,75</td><td style="border: 1px solid #ccc; padding: 4px;">238,59</td><td style="border: 1px solid #ccc; padding: 4px;">0,52%</td><td style="border: 1px solid #ccc; padding: 4px;">4,91</td><td style="border: 1px solid #ccc; padding: 4px;">4,89</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_6000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,973</td><td style="border: 1px solid #ccc; padding: 4px;">327,44</td><td style="border: 1px solid #ccc; padding: 4px;">48,81</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_7000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9804</td><td style="border: 1px solid #ccc; padding: 4px;">74,64</td><td style="border: 1px solid #ccc; padding: 4px;">213,49</td><td style="border: 1px solid #ccc; padding: 4px;">0,38%</td><td style="border: 1px solid #ccc; padding: 4px;">5,28</td><td style="border: 1px solid #ccc; padding: 4px;">5,27</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_7000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9767</td><td style="border: 1px solid #ccc; padding: 4px;">394,24</td><td style="border: 1px solid #ccc; padding: 4px;">40,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_8000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9823</td><td style="border: 1px solid #ccc; padding: 4px;">82,28</td><td style="border: 1px solid #ccc; padding: 4px;">193,59</td><td style="border: 1px solid #ccc; padding: 4px;">0,27%</td><td style="border: 1px solid #ccc; padding: 4px;">6,86</td><td style="border: 1px solid #ccc; padding: 4px;">6,83</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_8000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9797</td><td style="border: 1px solid #ccc; padding: 4px;">564,14</td><td style="border: 1px solid #ccc; padding: 4px;">28,33</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_9000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9837</td><td style="border: 1px solid #ccc; padding: 4px;">90,08</td><td style="border: 1px solid #ccc; padding: 4px;">176,96</td><td style="border: 1px solid #ccc; padding: 4px;">0,16%</td><td style="border: 1px solid #ccc; padding: 4px;">7,63</td><td style="border: 1px solid #ccc; padding: 4px;">7,61</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_9000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9821</td><td style="border: 1px solid #ccc; padding: 4px;">687,25</td><td style="border: 1px solid #ccc; padding: 4px;">23,25</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_10000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,9848</td><td style="border: 1px solid #ccc; padding: 4px;">97,64</td><td style="border: 1px solid #ccc; padding: 4px;">163,31</td><td style="border: 1px solid #ccc; padding: 4px;">0,08%</td><td style="border: 1px solid #ccc; padding: 4px;">8,38</td><td style="border: 1px solid #ccc; padding: 4px;">8,36</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OpenSearch</td><td style="border: 1px solid #ccc; padding: 4px;">100_10000_1</td><td style="border: 1px solid #ccc; padding: 4px;">0,984</td><td style="border: 1px solid #ccc; padding: 4px;">818,64</td><td style="border: 1px solid #ccc; padding: 4px;">19,53</td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;"></td>
                    </tr>
              </tbody>
            </table>
          <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><pre><code>GET <INDEX_NAME>/_search
{
  "query": {
    "knn": {
      "<DENSE_VECTOR_FIELD_NAME>": {
        "vector": [...],
        "k": <NUMBER_OF_CANDIDATES>,
        "method_parameters": {
          "ef_search": <NUMBER_OF_CANDIDATES>
        },
        "rescore": {
          "oversample_factor": <OVERSAMPLE>
        },
        "filter": {
          <SOME_FILTER>
        }
      }
    }
  },
  "size": <RESULT_SIZE>,
  "_source": {
    "excludes": [
      "<DENSE_VECTOR_FIELD_NAME>"
    ]
  }
}</code></pre><ul><li><code>&quot;size&quot;: &lt;RESULT_SIZE&gt;</code>: Número de resultados retornados ao cliente. Neste benchmark, o tamanho do resultado é 100 para calcular o Recall@100.</li><li><code>&quot;k&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: O número de candidatos a vizinhos mais próximos.</li><li><code>&quot;ef_search&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: O número de vetores a examinar.</li><li><code>&quot;oversample_factor&quot;: &lt;OVERSAMPLE&gt;</code>: Quantos vetores candidatos são recuperados antes da reclassificação.</li></ul><p>Elasticsearch</p><pre><code>GET <INDEX_NAME>/_search
{
  "query": {
    "knn": {
      "field": "<DENSE_VECTOR_FIELD_NAME>",
      "query_vector": [...],
      "k": <NUMBER_OF_CANDIDATES>,
      "num_candidates": <NUMBER_OF_CANDIDATES>,
      "rescore_vector": {
        "oversample": <OVERSAMPLE>
      },
      "filter": {
        <SOME_FILTER>
      }
    }
  },
  "size": <RESULT_SIZE>,
  "_source": {
    "excludes": [
      "<DENSE_VECTOR_FIELD_NAME>"
    ]
  }
}</code></pre><ul><li><code>&quot;size&quot;: &lt;RESULT_SIZE&gt;</code>: Número de resultados retornados ao cliente. Neste benchmark, o tamanho do resultado é 100 para calcular o Recall@100.</li><li><code>&quot;k&quot;: &lt;NUMBER_OF_CANDIDATES&gt;</code>: Número de vizinhos mais próximos a retornar de cada shard.</li><li><code>&quot;num_candidates&quot;: &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>.</li><li><code>&quot;oversample&quot;: &lt;OVERSAMPLE&gt;</code>: Quantos vetores candidatos são recuperados antes da reclassificação.</li></ul><p>Exemplo</p><p><code>Knn</code> consulta, (<code>100_500_1</code>), seria a seguinte:</p><p>OpenSearch</p><pre><code>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"
    ]
  }
}</code></pre><p>Elasticsearch</p><pre><code>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"
    ]
  }
}</code></pre><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f81002c9186e4c2d3e92f49d72418fee9860fc5e-761x401.png" style="width: 100%;" /><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>Um embedding vetorial denso de 128 dimensões utilizado para recuperação aproximada de kNN.</li><li>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.</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><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">O que é engenharia de contexto?</a></li><li><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></li><li><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></li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[jina-embeddings-v5-text: Incorporações de texto compactas e de última geração para busca e aplicações inteligentes]]></title>
            <description><![CDATA[Apresentação dos modelos jina-embeddings-v5-text models, including jina-embeddings-v5-text-small e jina-embeddings-v5-text-nano explicando como usar esses modelos de incorporação multilíngue por meio do Elastic Inference Service (EIS).]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/jina-embeddings-v5-text</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/jina-embeddings-v5-text</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Scott Martens, Sofia Vasileva]]></dc:creator>
            <pubDate>Mon, 23 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e98dd30ab925b4bb32830228d71a1a51d02a0917-1600x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A Jina AI e a Elastic estão lançando <code>jina-embeddings-v5-text</code>, uma família de novos modelos compactos de incorporação de texto de alto desempenho, com desempenho de última geração para modelos de tamanho comparável em todos os principais tipos de tarefas.</p><p>A família inclui dois modelos:</p><ul><li><code>jina-embeddings-v5-text-small</code></li><li><code>jina-embeddings-v5-text-nano</code></li></ul><p>Esses modelos são o resultado bem-sucedido de uma nova receita inovadora de treinamento para incorporação de modelos. Ambos superam modelos muitas vezes maiores que eles, gerando economia de memória e recursos computacionais e respondendo mais rápido a solicitações.</p><p>O modelo <code>jina-embeddings-v5-text-small</code> possui 677 milhões de parâmetros, é compatível com uma janela de contexto de entrada de 32.768 tokens e gera embeddings de 1.024 dimensões por padrão.</p><p><code>jina-embeddings-v5-text-nano</code> Pesa cerca de um terço do tamanho da nova versão, com 239 milhões de parâmetros e uma janela de contexto de entrada de 8.192 tokens, resultando em embeddings de dimensão 768 compactos.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Nome do modelo</td><td style="border: 1px solid #ccc; padding: 4px;">Tamanho total</td><td style="border: 1px solid #ccc; padding: 4px;">Tamanho da janela de contexto de entrada</td><td style="border: 1px solid #ccc; padding: 4px;">Tamanho do embedding</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">jina-v5-text-small</td><td style="border: 1px solid #ccc; padding: 4px;">677M params</td><td style="border: 1px solid #ccc; padding: 4px;">32.768 tokens</td><td style="border: 1px solid #ccc; padding: 4px;">1.024 dims</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">jina-v5-text-nano</td><td style="border: 1px solid #ccc; padding: 4px;">239M params</td><td style="border: 1px solid #ccc; padding: 4px;">8.192 tokens</td><td style="border: 1px solid #ccc; padding: 4px;">768 dimensões</td>
                    </tr>
              </tbody>
            </table>
          <p>Esses dois modelos são os melhores da categoria para o desempenho geral do benchmark MMTEB (<a href="https://huggingface.co/spaces/mteb/leaderboard">Multilingual MTEB</a>). Entre os modelos com menos de 500M, <code>jina-embeddings-v5-text-nano</code> é o de melhor desempenho, apesar de ter menos de 250M, e o modelo <code>jina-embeddings-v5-text-small</code> é o líder entre os modelos de embedding multilíngue com menos de 750M.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d97126285fa196c3045b1913b7754e4b186c1e4c-1300x1100.png" style="width: 100%;" /><p>Esses modelos estão disponíveis por meio do Elastic Inference Service (EIS), de uma API online e para hospedagem local. Para instruções sobre como acessar os modelos <code>jina-embeddings-v5-text</code>, veja a seção &quot;<strong>Começar</strong>&quot; abaixo.</p><p>Modelos de incorporação e indexação semântica aumentam muito a precisão dos algoritmos de busca, mas também têm uma variedade de outros usos para tarefas envolvendo similaridade semântica e extração de significado, por exemplo:</p><ul><li>Encontrando textos duplicados.</li><li>Reconhecendo paráfrases e traduções.</li><li>Descoberta de tópicos.</li><li>Mecanismos de recomendação.</li><li>Análise de sentimentos e intenções.</li><li>Filtragem de spam.</li><li>E muitos outros.</li></ul><h2><strong>Recursos</strong></h2><p>Essa nova família de modelos possui uma série de recursos projetados para aumentar a relevância e reduzir custos.</p><h3>Otimização de tarefas</h3><p>Otimizamos os modelos <code>jina-embeddings-v5-text</code> para quatro tipos amplos de tarefas:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Tarefa</td><td style="border: 1px solid #ccc; padding: 4px;">Exemplos de casos de uso</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Recuperação</td><td style="border: 1px solid #ccc; padding: 4px;">Busca com consultas em linguagem natural e recuperação das correspondências mais relevantes em um conjunto de documentos.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Correspondência de texto</td><td style="border: 1px solid #ccc; padding: 4px;">Similaridade semântica, desduplicação, alinhamento de paráfrases e traduções, e muito mais.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Clustering</td><td style="border: 1px solid #ccc; padding: 4px;">Descoberta de tópicos, organização automática de coleções de documentos.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Classificação</td><td style="border: 1px solid #ccc; padding: 4px;">Categorização de documentos, detecção de sentimentos e intenções, tarefas similares.</td>
                    </tr>
              </tbody>
            </table>
          <p>Otimizar para uma tarefa geralmente significa ter que ceder em outra, então a maioria dos modelos de embedding só tem desempenho competitivo para um tipo de tarefa. Mas os modelos <code>jina-embeddings-v5-text</code> são capazes de se especializar em todas as quatro áreas sem comprometer o desempenho, treinando <a href="https://arxiv.org/abs/2106.09685">adaptadores compactos de Low-Rank Adaptation (LoRA)</a> específicos para cada tarefa.</p><p>Adaptadores LoRA são uma espécie de plugin para um modelo de IA que muda o comportamento, aumentando ligeiramente o tamanho total. Em vez de ter um modelo inteiro para cada tarefa, cada um com centenas de milhões de parâmetros, a família de modelos <code>jina-embeddings-v5-text</code> permite que você use um modelo com um adaptador compacto LoRA para cada tarefa. Isso economiza memória, espaço de armazenamento e custos de inferência.</p><h3>Truncando embeddings</h3><p>Treinamos os modelos <code>jina-embeddings-v5-text</code> usando o <a href="https://arxiv.org/abs/2205.13147">Aprendizado de representação Matryoshka</a>, que permite reduzir seus embeddings para tamanhos menores com um custo mínimo para a qualidade deles.</p><p>Por padrão, <code>jina-embeddings-v5-text-small</code> gera vetores de embedding de 1024 dimensões, cada um representado por um número de 16 bits, fazendo com que cada embedding tenha 2KB de tamanho. Para um grande conjunto de documentos, isso pode representar uma grande quantidade de dados para armazenar, e a busca em um banco de dados vetorial repleto de embeddings é proporcional tanto ao tamanho do banco de dados quanto ao número de dimensões que cada vetor armazenado possui.</p><p>Mas você pode reduzir pela metade o tamanho dos embeddings (descartar 512 das 1024 dimensões) e ocupar metade do espaço enquanto dobra a velocidade das buscas. Isso tem um impacto no desempenho. Descartar informações reduz a precisão. Mas, como mostra o gráfico abaixo, mesmo ao eliminar metade do embedding, a redução de desempenho é mínima:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f11969682c81bcf22b5b29a6204ba6ec40471b3d-900x700.png" style="width: 100%;" /><p>Desde que suas embeddings tenham pelo menos 256 dimensões, a perda de precisão deve permanecer relativamente pequena. Abaixo desse nível, porém, a relevância e a precisão se deterioram rapidamente.</p><p>Truncar embeddings como esse permite aos usuários gerenciar as próprias trocas entre precisão e custos computacionais. Ela oferece ferramentas para ter grandes ganhos de eficiência e grandes economias de custos com a IA de busca.</p><h3>Quantização robusta</h3><p><em>Quantização </em>é outra forma de reduzir o tamanho das embeddings. Em vez de descartar parte de cada incorporação, a quantização reduz a precisão dos números na embedding. Os modelos <code>jina-embeddings-v5-text</code> geram embeddings com números de 16 bits, mas podemos arredondar esses números, reduzindo a precisão e o número de bits necessários para armazená-los. No caso mais extremo, podemos reduzir cada número a um bit (0 ou 1), comprimindo as embeddings padrão de 1024 dimensões de <code>jina-embeddings-v5-text</code>de 2 kilobytes para 128 bytes, uma redução de 94% apenas com a quantização binária. Assim como para a truncagem, isso produz grandes economias em memória e custos computacionais. No entanto, assim como a truncagem, a quantização torna as embeddings menos precisas.</p><p>Treinamos os modelos <code>jina-embeddings-v5-text</code> para funcionar com a <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/bbq">Better Binary Quantization (BBQ)</a> do <a href="https://www.elastic.co/pt/elasticsearch">Elasticsearch</a>, minimizando a perda de precisão. Os testes de benchmark de embeddings binarizados desses modelos mostram desempenho quase igual aos equivalentes não binarizados. Consulte <a href="https://arxiv.org/abs/2602.15547">o relatório técnico</a> para estudos detalhados de ablação sobre o desempenho da binarização.</p><h3>Desempenho multilíngue</h3><p>Muitos modelos de embedding são multilíngues porque foram treinados com materiais que incluem um grande número de linguagens. Mas isso não significa que todos tenham o mesmo desempenho em todas as linguagens disponíveis.</p><p>Identificamos 211 linguagens no benchmark multilíngue MMTEB e os separamos para que pudéssemos comparar nossos modelos com modelos semelhantes linguagem por linguagem. A imagem abaixo resume nossos resultados como um mapa de calor. Cada patch é uma linguagem (identificada pelo código ISO-639), e quanto mais verde ela é, melhor o modelo teve desempenho em comparação com a média de modelos similares:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/852ac5d0f1977bb0c1124d87f8863a9bb94eb7da-1600x765.png" style="width: 100%;" /><p>Embora a precisão varie entre as linguagens, os modelos <code>jina-embeddings-v5-text</code> são de ponta ou quase isso na maioria das linguagens do mundo.</p><p>Para saber detalhes sobre o desempenho multilíngue, consulte o <a href="https://arxiv.org/abs/2602.15547"><code>jina-embeddings-v5-text</code> relatório técnico</a>.</p><h2><strong>Jina no Elastic: IA nativa de última geração para busca</strong></h2><p>Com <code>jina-embeddings-v5-text</code> modelos no EIS, você pode executar modelos de embedding multilíngue de alto desempenho de forma nativa no <a href="https://www.elastic.co/pt/elasticsearch">Elasticsearch</a>, com inferência totalmente gerenciada e acelerada por GPU, sem infraestrutura para provisão ou redimensionamento. <code>jina-embeddings-v5-text</code> modelos ampliam o catálogo cada vez maior de modelos EIS com modelos compactos e multilíngues, impulsionados pelos mais recentes avanços em IA. Esses modelos têm desempenho de ponta em recuperação de informações e benchmarks padrão de análise de dados, além de oferecer suporte multilíngue incomparável e global.</p><p>Com dois modelos de tamanhos muito diferentes, os usuários podem definir qual é o mais adequado para as aplicações e orçamentos. Além disso, com embeddings robustas que mantêm o desempenho quando truncadas para tamanhos menores ou quantizadas com menor precisão, <code>jina-embeddings-v5-text </code>modelos oferecem oportunidades para economias concretas adicionais em custos de armazenamento e computação, bem como na latência de processamento.</p><p>Com a família <code>jina-embeddings-v5-text</code> , Jina Reranker e a busca vetorial rápida e BM25 da Elastic, os usuários agora têm acesso à <a href="https://www.elastic.co/docs/solutions/search/hybrid-search">busca híbrida</a> de ponta a ponta e de última geração da Elastic. Quando você precisa dos resultados mais relevantes, seja para pipelines de Retrieval-Augmented Generation (RAG), aplicações de busca ou análise de dados, a Elastic com modelos de IA de busca Jina oferece qualidade sólida e econômica.</p><h2><strong>Para começar</strong></h2><p>Os modelos <code>jina-embeddings-v5-text</code> estão totalmente integrados ao <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a> e você pode usá-los definindo o <strong><code>type</code> </strong>campo para<strong> <code>semantic_text</code></strong> ao criar seu índice e especificar o modelo (<code>jina-embeddings-v5-text-small</code> ou <code>jina-embeddings-v5-text-nano</code>) no <code>inference_id</code><strong> </strong>campo, como neste exemplo:</p><pre><code>PUT multilingual-semantic-index
{
  "mappings": {
    "properties": {
      "content": {
        "type": "semantic_text",
        "inference_id": ".jina-embeddings-v5-text-small"
      }
    }
  }
}

# Ingest data about France
POST multilingual-semantic-index/_doc
{
  "content": "The capital of France is Paris"}

GET multilingual-semantic-index/_search
{
  "query": {
    "semantic": {
      "field": "content",
      "query": "What is the French capital?"
    }
  }
}</code></pre><p>O <a href="https://www.elastic.co/pt/elasticsearch">Elasticsearch</a> seleciona automaticamente o adaptador LoRA apropriado durante a indexação e a recuperação. As dimensões de embedding (veja a seção &quot;<strong>Truncando embeddings</strong> &quot;, acima) podem ser definidas ao <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">criar um endpoint de inferência personalizado</a>.</p><p>Consulte a <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dense-vector">documentação do Elasticsearch</a> para saber mais informações sobre como usar os modelos <strong><code>jina-embeddings-v5-text</code> </strong> .</p><h2><strong>Mais informações</strong></h2><p>Para saber mais sobre os modelos <code>jina-embeddings-v5-text</code> , leia as <a href="https://jina.ai/news/jina-embeddings-v5-text-distilling-4b-quality-into-sub-1b-multilingual-embeddings/">notas de lançamento no blog da Jina AI</a> e o <a href="https://arxiv.org/abs/2602.15547">relatório técnico</a>, que contém informações técnicas mais detalhadas sobre o desempenho e o novo procedimento de treinamento inovador da Jina AI. Para saber informações sobre como fazer download e executar esses modelos de forma local, acesse a<a href="https://huggingface.co/collections/jinaai/jina-embeddings-v5-text"> página da coleção</a> <a href="https://huggingface.co/collections/jinaai/jina-embeddings-v5-text"><code>jina-embeddings-v5-text</code></a>na Hugging Face.</p><p>Os modelos de Jina AI estão disponíveis sob a <a href="https://spdx.org/licenses/CC-BY-NC-4.0">licença CC-BY-NC-4.0</a>, portanto, você pode baixá-los e experimentá-los de forma livre, mas para uso comercial, entre em contato com a <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">equipe de vendas da Elastic</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Garantindo precisão semântica com pontuação mínima]]></title>
            <description><![CDATA[Melhore a precisão semântica empregando limiares mínimos de pontuação. O artigo inclui exemplos concretos de busca semântica e híbrida. ]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/semantic-precision-minimum-score</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/semantic-precision-minimum-score</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Mattias Brunnert]]></dc:creator>
            <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8b3b5910abfe16d48d309341a0027008b16c4340-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A busca semântica abriu um mundo de oportunidades para a relevância da busca. Modelos esparsos e densos de alta qualidade, como ELSER, E5 e Jina Embedding v4, retornam resultados relevantes com base no significado das palavras, em vez da correspondência de palavras-chave. No entanto, a busca semântica às vezes retorna resultados irrelevantes na cauda final ou para consultas que não apresentam resultados relevantes no índice. Essa propriedade dos modelos esparsos e densos pode confundir os usuários ou desperdiçar tokens preciosos para grandes modelos de linguagem (LLMs).</p><p>Neste artigo, você aprenderá como usar o parâmetro de pontuação mínima para aumentar a precisão dos seus resultados de busca semânticos. Se você quiser testar os exemplos fornecidos neste post do blog, <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/ensuring-semantic-precision-with-minimum-score/ensuring_semantic_precision_with_minimum_score.ipynb">acesse o caderno Jupyter associado</a>.</p><h2>Contexto: Precisão e recall</h2><p>Na relevância da pesquisa, a <em>precisão </em>e a <em>recall </em>são conceitos-chave. Qualquer leitor que ainda não esteja familiarizado é altamente incentivado a pesquisar sobre eles. Segue abaixo um resumo.</p><ul><li><strong>Precisão: </strong>a fração dos resultados de busca retornados que são relevantes para o usuário.</li><li><strong>Recall: </strong>a fração de todos os documentos relevantes no corpus que estão incluídos no conjunto de resultados de busca.</li></ul><p>Ou, em outras palavras, a precisão retorna <strong>apenas </strong>resultados relevantes e o recall retorna <strong>todos </strong>os resultados relevantes. Como você pode imaginar, esses são requisitos frequentemente concorrentes. A busca semântica tende a ter uma memória muito alta, mas pode ter dificuldades com precisão. Continue lendo para saber como se locomover por esta propriedade.</p><h2>Apresentando o parâmetro de pontuação mínima</h2><p>O parâmetro ‘min_score’ nos permite melhorar a precisão ao definir uma pontuação mínima, que truncará o conjunto de resultados removendo quaisquer correspondências com uma pontuação inferior ao limite definido. Aqui está um exemplo simples:</p><pre><code>GET search-movies/_search
{
  "retriever": {
    "linear": {
      "min_score": 4,
      "retrievers": [
        ...
      ]
    }
  }
}</code></pre><h2>Normalização da pontuação</h2><p>Definir uma pontuação mínima é muito bom; no entanto, nem todos os modelos semânticos retornam uma pontuação adequada para um limite estático. ELSER, por exemplo, retorna uma pontuação que é ilimitada. <a href="https://huggingface.co/intfloat/e5-small#faq">Algumas</a> pontuações de modelos densos estão fortemente agrupadas e só fazem sentido no contexto da consulta específica.</p><p>Para a maioria dos casos de busca semântica, recomendamos usar uma abordagem de normalização antes de aplicar o &#x27;min_score&#x27;. A normalização garante que a pontuação do documento esteja dentro de um intervalo definido. Os recuperadores Elasticsearch fornecem dois desses <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever#linear-retriever-normalizers">normalizadores</a>, &#x27;l2_norm&#x27; e &#x27;minmax&#x27;. O mais comumente usado é o &#x27;minmax&#x27;, pois é fácil de entender e funciona bem em muitos cenários. As principais propriedades do &#x27;minmax&#x27; incluem:</p><ul><li>As pontuações dos documentos são distribuídas entre 0 e 1.</li><li>O documento com maior pontuação é sempre pontuado como 1.</li><li>O documento com menor pontuação sempre é pontuado como 0.<ul><li>Isso pode torná-lo menos adequado para buscar palavras-chave. Consulte a seção “Busca híbrida” para uma discussão mais aprofundada.</li></ul></li></ul><p>A seguir está um exemplo de consulta semântica normalizada com <code>min_score</code>. O tamanho da janela de classificação foi aumentado para 500 para permitir que possamos retornar uma lista maior de resultados de busca, começando em 100.</p><pre><code>GET search-movies/_search
{
  "size": 100,
  "_source": [
    "title", "overview"
  ],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><p>O tamanho foi ajustado para um valor maior do que o normalmente visto na produção. Isso é para que possamos inspecionar a qualidade dos resultados de busca e ajustar os resultados.</p><h2>Busca híbrida usando o recuperador linear</h2><p>Para busca híbrida, a abordagem mais simples é normalizar todas as pontuações, atribuir pesos e aplicar uma pontuação mínima. Note que, ao escolher pesos cuja soma seja 1, você mantém a pontuação total dentro de um intervalo de 0 a 1. Isso facilita entender as pontuações finais e afinar <code>min_score</code>. A seguir está um exemplo:</p><pre><code>GET search-movies/_search
{
  "size": 100,
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "linear": {
      "rank_window_size": 500,
      "min_score": 0.25,
      "retrievers": [
        {
          "weight": 0.6,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "overview_vector",
                  "query": "superhero movie"
                }
              }
            }
          }
        },
        {
          "weight": 0.4,
          "normalizer": "minmax",
          "retriever": {
            "standard": {
              "query": {
                "multi_match": {
                  "query": "superhero movie",
                  "fields": ["overview","keywords", "title"],
                  "type": "cross_fields",
                  "minimum_should_match": "2"
                }
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><h2>Busca híbrida usando o RRF</h2><p>Com o BM25, muitas vezes controlamos a precisão por outros meios, como usando o operador <code>AND</code> ou <code>minimum_should_match</code>. Além disso, consultas compostas por termos únicos, precisos e raros naturalmente causam resultados de busca com poucos resultados, muitas vezes todos altamente relevantes. Isso pode resultar em:</p><ul><li>Os resultados mais distantes na lista recebem uma pontuação normalizada baixa no recuperador BM25, mesmo que a pontuação absoluta do BM25 esteja próxima das pontuações mais altas.</li><li>Ao adicionar uma pontuação BM25 muito baixa à pontuação semântica, o total pode ser aproximado como a pontuação semântica.</li><li>A falta de contribuição da pontuação BM25 pode fazer com que o documento seja descartado pelo <code>min_score threshold</code>.</li></ul><p>Como solução, podemos usar a fusão de classificação recíproca (RRF) para combinar os resultados BM25 e semânticos. O RRF contorna o desafio de comparar pontuações de diferentes algoritmos de busca focando na posição em cada conjunto de resultados. Nesse cenário, o <code>min_score</code> é aplicado apenas ao recuperador semântico.</p><pre><code>GET search-movies/_search
{
  "_source": ["title", "overview","keywords"],
  "retriever": {
    "rrf": {
      "rank_window_size": 500,
      "retrievers": [
        {
          "linear": {
            "rank_window_size": 500,
            "min_score": 0.25,
            "retrievers": [
              {
                "normalizer": "minmax",
                "retriever": {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "overview_vector",
                        "query": "superhero movie"
                      }
                    }
                  }
                }
              }
            ]
          }
        },
        {
          "standard": {
            "query": {
              "multi_match": {
                "query": "superhero movie",
                "fields": ["overview", "keywords","title"],
                "type": "cross_fields",
                "minimum_should_match": "2"
              }
            }
          }
        }
      ]
    }
  }
}</code></pre><h2>Conclusão</h2><p>Usando <code>min_score</code>, mostramos como podemos reduzir o número de falsos positivos em nossos conjuntos de resultados causados pela alta recordação de algoritmos de busca semântica. Para saber mais sobre recuperadores, consulte este <a href="https://www.elastic.co/search-labs/blog/elasticsearch-retrievers">post do blog</a> e a <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">documentação do Elasticsearch</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gerenciamento de dependências no Kubernetes]]></title>
            <description><![CDATA[Como simplificar o gerenciamento de dependências no Kubernetes usando a CLI do Renovate e os fluxos de trabalho do Argo.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/dependency-management-kubernetes</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/dependency-management-kubernetes</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Nikos Fotiou]]></dc:creator>
            <pubDate>Thu, 19 Feb 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00519840e6eec7101c1fb096afcae976ee0c454e-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Foi assim que construímos uma plataforma de gerenciamento de dependências auto-hospedada usando Kubernetes, Argo Workflows, Argo Events e CLI de Renovate para automatizar atualizações, corrigir de forma rápida vulnerabilidades e exposições comuns (CVEs) e propagar com eficiência novas versões de pacotes em milhares de repositórios.</p><h2><strong>Gerenciamento de dependências no Elastic</strong></h2><p>Na Elastic, precisamos gerenciar centenas ou até milhares de repositórios, tanto privados quanto públicos. Quando um CVE crítico é descoberto, precisamos de respostas e ações imediatas: quais repositórios são vulneráveis? Com que rapidez podemos corrigir os problemas? Além da segurança, também surgem questões de produtividade: como podemos propagar de forma rápida o lançamento de uma nova versão do pacote em todos os repositórios que dependem dela sem gastar muito tempo em tarefas manuais?</p><p>O gatilho inicial para pesquisar maneiras de fazer o gerenciamento de dependências foi a necessidade de estabelecer uma base segura com atualizações automatizadas para <a href="https://www.elastic.co/blog/reducing-cves-in-elastic-container-images">reduzir os CVEs</a>. Após considerar cuidadosamente soluções para gerenciamento de dependências, começamos a trabalhar em uma infraestrutura auto-hospedada. Estávamos usando nosso próprio cluster Kubernetes para executar o Mend Renovate Community Self-Hosted. A ideia era fornecer uma plataforma de gerenciamento de dependências que nossos usuários pudessem acessar de forma autônoma.</p><p>O experimento inicial foi bem-sucedido, então mais e mais equipes começaram a integrar nossa plataforma e usá-la no ciclo de vida diário dos repositórios para atualizações e patches de CVE. Isso aconteceu tão rápido que logo chegamos ao limite da nossa instalação auto-hospedada.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e14d9f98d4eccaa08a335d5bd23d88e5debbb344-1600x1103.png" style="width: 100%;" /><h3><strong>O desafio: como podemos redimensionar uma plataforma de gerenciamento de dependências em uma grande organização com um número significativo de repositórios?</strong></h3><p>Nossa plataforma de gerenciamento de dependências estava processando um repositório por vez e o modelo de processamento sequencial não conseguia acompanhar, devido ao grande número de repositórios que possuímos. Já havíamos identificado que o problema residia no conceito de que <strong>uma única instância</strong> de nossa ferramenta de gerenciamento de dependências poderia processar nossa grande e crescente lista de repositórios. Repositórios aguardavam em uma fila, às vezes por muitas horas. Mais de 50% dos nossos repositórios nem sequer eram processados diariamente. Isso significa que mais de 50% dos nossos repositórios esperaram mais de 24 horas entre as varreduras.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/45ade5bda08f82bed0b3d0d3736cbd6f056e7a4e-1312x816.jpg" style="width: 100%;" /><p>Repositórios grandes criavam gargalos maiores, devido às bases de código consideráveis e aos múltiplos PRs abertos. Eventos do webhook do GitHub interromperam a sequência. O Automerge tornou-se não confiável porque o tempo de varredura era imprevisível. Fizemos uma promessa aos nossos usuários sobre a frequência dos escaneamentos, mas não conseguimos cumpri-la.</p><h3><strong>A decisão de criar internamente: atendendo às necessidades únicas de escala e segurança da Elastic</strong></h3><p>Enquanto considerávamos opções comerciais, incluindo a <strong>edição Mend&#x27;s Renovate Self-Hosted Enterprise Self-Hosted</strong>, internamente na Elastic tivemos algumas iniciativas-chave em desenvolvimento.</p><p>Nossa decisão de criar uma plataforma interna foi motivada pelo reconhecimento de que somente uma solução personalizada poderia atender aos requisitos específicos e inegociáveis da Elastic:</p><ol><li><strong>Investindo em nossa plataforma interna de desenvolvedores:</strong> naquela época, já tínhamos começado a investir fortemente em nossa plataforma interna de desenvolvedores. Estávamos discutindo e projetando formas de como cada um dos nossos serviços poderia se encaixar nisso. Isso significava que queríamos testar nossas próprias regras e práticas para nossa plataforma de gerenciamento de dependências. Além disso, novas diretrizes estavam entrando em ação e queríamos projetar a plataforma antes dos eventos.</li><li><strong>Integração nativa e personalização do fluxo de trabalho:</strong> precisávamos de uma integração direta com nossas ferramentas e processos internos. Por exemplo, queríamos centralizar a configuração como código com nosso Catálogo de serviços (Backstage). Temos necessidades específicas relacionadas ao uso do Backstage que queríamos tornar compatíveis com nossa plataforma. Portanto, embora fosse possível usar as APIs Renovate Self-Hosted junto com nossa automação Backstage, isso não cobriria totalmente nossos processos internos.</li><li><strong>Segurança de defesa em profundidade específica da Elastic:</strong> nossa rigorosa conformidade de segurança exigiu mecanismos de segurança personalizados, adaptados ao nosso ecossistema. Estávamos trabalhando para <a href="https://entro.security/blog/how-elastic-scaled-secrets-nhi-security-elastics-playbook-from-visibility-to-automation/">fortalecer nosso uso de &quot;identidades não humanas&quot;.</a> A forma como esse reforço de acesso funcionava significava que os métodos não padronizados de autenticação no GitHub não funcionariam com uma ferramenta comercial que não suportasse essa implementação interna. Nosso fluxo de trabalho incluía a implementação de um padrão de criptografia secreta de fluxo de trabalho pai-filho e o uso de tokens transitórios e de uso único do GitHub. Criar internamente foi a única maneira prática de incorporar essas camadas de segurança exclusivas e minimizar a superfície de ataque em nosso complexo ambiente multinuvem.</li></ol><h2><strong>A solução: orquestração de fluxo de trabalho para gerenciamento de dependências</strong></h2><p>Nossa solução começou com o fato de queríamos criar sobre a ferramenta de gerenciamento de dependências que já usávamos e não substituí-la, buscando outras soluções. Ela já demonstrava sinais de potencial, e a flexibilidade é importante para diferentes necessidades em toda a organização. Consideramos diferentes soluções, e o que nos ajudou a decidir foram as necessidades, às vezes grandes e especiais, que precisamos cobrir. Decidimos criar uma plataforma de gerenciamento de dependências confiável e escalável, na qual cada repositório será processado por conta própria, removendo gargalos e nos preparando para o crescimento.</p><p>Projetamos a plataforma seguindo três princípios fundamentais:</p><h3><strong>1. Processamento paralelo</strong></h3><p>Cada repositório recebe o próprio ambiente de processamento de gerenciamento de dependências. Não há mais filas. Nossa concorrência é limitada apenas pelo número de recursos que gastamos. Também aplicamos o agendamento distribuído inteligente para evitar que o GitHub limite a taxa.</p><h3><strong>2. Autoatendimento</strong></h3><p>Usamos nosso Catálogo de serviços (Backstage) para integrar e gerenciar automaticamente qualquer novo repositório. Usamos nossa própria definição de recursos para dar ao usuário final a opção de selecionar com que frequência um repositório será processado, quantos recursos deseja alocar para os cronogramas e se deseja desligar ou reativar o processamento por qualquer motivo. Planejamos adicionar mais opções assim conforme as necessidades dos nossos usuários evoluem e eles se familiarizam com a nova instalação.</p><h3><strong>3. Redução do escopo secreto e isolamento do espaço de nome</strong></h3><p>Para mais segurança, fornecemos aos nossos pods de gerenciamento de dependências tokens efêmeros do GitHub que são gerados no início de cada fluxo de trabalho. Além disso, isolamos nossas cargas de trabalho em espaços de nome específicos para que possam receber apenas os segredos necessários. Controlamos quais segredos podem ser acessados em cada fluxo de trabalho de gerenciamento de dependências usando o Kubernetes RBAC. Também usamos criptografia para propagar o token do GitHub do fluxo de trabalho pai para o filho.</p><p>Reconstruímos nossa plataforma usando e aproveitando o melhor de Kubernetes, do Argo Workflows que alimenta a lógica dos nossos processos, e a CLI do Renovate que está configurado para escanear e processar um repositório de cada vez.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5560ed20e2bd9ecdd574a9c835126d12b24c332f-1600x1157.png" style="width: 100%;" /><p><strong>A beleza:</strong> estamos utilizando projetos open source testados de forma inovadora, fornecendo novos exemplos práticos para todos esses projetos e, ao mesmo tempo, ampliando a velocidade de desenvolvimento e consolidando a redução de CVE para nossas equipes.</p><h2><strong>Arquitetura de gerenciamento de dependências: quatro microsserviços</strong></h2><p>A plataforma é composta por quatro componentes personalizados:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b3d4046c05bb261e45d40c59f864eb51fb9eaa9-1217x1600.png" style="width: 100%;" /><h3><strong>Operador de fluxos de trabalho (Go/Kubebuilder)</strong></h3><p>Um operador do Kubernetes gerenciando o ciclo de vida do fluxo de trabalho por meio de três definições de recursos personalizadas (CRDs):</p><ul><li><strong>RepoConfig CRD:</strong> fonte única de verdade para configuração de repositórios.</li></ul><p>É assim que o RepoConfig é definido no operador:</p><pre><code>// RepoConfig is the Schema for the repoconfigs API
type RepoConfig struct {
	metav1.TypeMeta `json:",inline"`

	// metadata is a standard object metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty,omitzero"`

	// spec defines the desired state of RepoConfig
	// +required
	Spec RepoConfigSpec `json:"spec"`

	// status defines the observed state of RepoConfig
	// +optional
	Status RepoConfigStatus `json:"status,omitempty,omitzero"`
}</code></pre><p>E essa é a aparência de uma instância do RepoConfig:</p><pre><code>apiVersion: workflows.elastic.co/v1
kind: RepoConfig
metadata:
  generation: 3
  name: elastic-test-repo
  namespace: dependency-management-operator
spec:
  owner: group:my-team
  renovate:
    config:
      resourceGroup: SMALL
      runFrequency: 4h
    enabled: true
  repository: elastic/test-repo</code></pre><ul><li><strong>CRD pai:</strong> gerencia os fluxos de trabalho do CronWorkflows para varreduras agendadas.</li></ul><p>Dentro do loop de reconciliação do controlador principal, garantimos que as configurações de fluxo de trabalho sejam criadas e mantidas atualizadas ou até mesmo excluídas, se necessário.</p><p>Primeiro, ele recebe algumas configurações globalmente configuradas para fluxos de trabalho:</p><pre><code>func (r *ParentReconciler) reconcileSubResources(ctx context.Context, req ctrl.Request, parent *workflowsv1.Parent) error {
	logger := logf.FromContext(ctx)
	logger.Info("Reconcile SubResources for Parent", "name", req.NamespacedName)
	wfSet := workflowsettings.WorkflowSettings{
		RunFrequency:   parent.Spec.RunFrequency,
		ResourceGroups: "parent",
	}</code></pre><p>Ele garante que um mutex configmap esteja atualizado para evitar fluxos de trabalho semelhantes rodando juntos:</p><pre><code>	cfMngr := resources.NewConfigMapManager(r.Client, r.Scheme, r.OperatorConfig.ParentNamespace)
	err := cfMngr.CreateOrUpdateSyncMutexConfigmap(ctx, fmt.Sprintf("%s%s", r.OperatorConfig.ResourcesPrefix, r.OperatorConfig.SyncMutexCfgMapName), strings.TrimPrefix(parent.Spec.Repository, "elastic/"), r.OperatorConfig.SemaphoreConcurrencyLimit)</code></pre><p>Depois, cria um Gerenciador de fluxo de trabalho que é a estrutura que criará ou atualizará os CronWorkflows e os Modelos de fluxo de trabalho:</p><pre><code>	wfMngr := resources.NewArgoWorkflowManager(r.Client,
		r.Scheme,
		curateResourceName(
			strings.ReplaceAll(parent.Spec.Repository, "/", "-"),
		),
		parent.Namespace,
		"parent-workflow",
		false).
		WithOrganization(r.OperatorConfig.GitHubOrg).
		WithRepoName(parent.Spec.Repository).
		Init(true, true).
		WithPrefix(r.OperatorConfig.ResourcesPrefix).
		WithWfTemplateName(r.OperatorConfig.ParentWorkflowTemplate).
		WithResources(wfSet.GetResourceCategory()).
		WithSchedule(wfSet.GetCronSchedule()).
		WithImagePullSecrets([]corev1.LocalObjectReference{{
			Name: r.OperatorConfig.WorkflowImagePullSecrets,
		}}).
		AddArgument(true, true, "extra_cli_args").
		SetArgument(true, false, "extra_cli_args", "none").
		AddTemplate(resources.NewParentDAGTemplateInstance()).
		AddTemplate(resources.NewWorkflowsTemplateInstance("check-child-workflows", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddTemplate(resources.NewWorkflowsTemplateInstance("security", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddTemplate(resources.NewWorkflowsTemplateInstance("submit-child-workflow", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector))
	wfMngr.OverWriteCommand("submit-child-workflow", r.OperatorConfig.ChildNamespace)
	wfMngr.OverwriteWfTemplateName("parent-wftmpl")
	wfMngr.AddSynchronization(fmt.Sprintf("%s%s", r.OperatorConfig.ResourcesPrefix, r.OperatorConfig.SyncMutexCfgMapName), "{{workflow.parameters.repo_name}}")
	err = wfMngr.CreateOrUpdateCronWorkflow(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update cron workflow: %w", err)
	}
	err = wfMngr.CreateOrUpdateWorkflowTemplate(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update workflow template: %w", err)
	}
	return nil</code></pre><ul><li><strong>Child CRD:</strong> gerencia WorkflowTemplates com recursos por repositório.</li></ul><p>O controlador filho tem uma função de reconciliação semelhante à do controlador pai, mas desta vez é responsável pelos modelos de fluxo de trabalho no espaço de nome filho que serão acionados pelos fluxos de trabalho pai.</p><pre><code>func (r *ChildReconciler) reconcileSubResources(ctx context.Context, req ctrl.Request, child *workflowsv1.Child) error {
	logger := logf.FromContext(ctx)
	logger.Info("Reconcile SubResources for Child", "name", req.NamespacedName)
	wfSet := workflowsettings.WorkflowSettings{
		ResourceGroups: child.Spec.ResourceCategory,
	}
	wfMngr := resources.NewArgoWorkflowManager(r.Client,
		r.Scheme,
		curateResourceName(
			strings.ReplaceAll(child.Spec.Repository, "/", "-"),
		),
		child.Namespace,
		"runner",
		true).
		Init(false, true). // only manage workflow template
		WithPrefix(r.OperatorConfig.ResourcesPrefix).
		WithSuffix("-child-wftmpl").
		WithRepoName(child.Spec.Repository).
		WithOrganization(r.OperatorConfig.GitHubOrg).
		WithResources(wfSet.GetResourceCategory()). // will override resources of presets if set
		WithImagePullSecrets([]corev1.LocalObjectReference{{
			Name: r.OperatorConfig.WorkflowImagePullSecrets,
		}}).
		AddTemplate(resources.NewWorkflowsTemplateInstance("runner", r.OperatorConfig.WorkflowImagePullPolicy, r.OperatorConfig.WorkflowNodeSelector)).
		AddArgument(false, true, "repo_full_name").
		AddArgument(false, true, "repo_name").
		AddArgument(false, true, "encrypted_token").
		AddArgument(false, true, "extra_cli_args")
	wfMngr.OverWriteCommand("runner", r.OperatorConfig.ChildNamespace)
	err := wfMngr.CreateOrUpdateWorkflowTemplate(ctx)
	if err != nil {
		return fmt.Errorf("failed to create or update workflow template: %w", err)
	}
	return nil
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ac70492a1266ba02cb8afbafc5a486cb38a0edc-1600x1290.png" style="width: 100%;" /><p>O padrão multicontrolador proporciona separação clara: o RepoConfig Controller cuida do onboarding/offboarding, o Parent Controller gerencia o escalonamento e o Child Controller gerencia os templates de execução.</p><h3><strong>GitHub Events Gateway (Go)</strong></h3><p>Um proxy seguro de webhook que recebe webhooks do GitHub, verifica assinaturas, filtra por organização/repositório e direciona para os eventos do Argo. Criamos 10 sensores distintos que respondem a interações com dashboards de dependência, eventos de PR e atualizações de pacotes.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4828625456cbd6efa8020a20f10d23f294f98a02-1306x1600.png" style="width: 100%;" /><p>Esse gateway permite a integração com os apps do GitHub por meio de:</p><ul><li>Verificação de assinaturas de webhooks do GitHub recebidas para fins de segurança.</li><li>Encaminhando eventos válidos para o Argo Events EventSource com todos os cabeçalhos e autenticação relevantes.</li><li>Nós também configuramos um authSecret no EventSource e fornecemos isso como um cabeçalho Bearer nas requisições encaminhadas.</li><li>Fornecer loggings, métricas e lógica de repetição.</li></ul><p>Ele realiza diversas validações em cada solicitação de evento do GitHub.</p><p>Ele garante que alguns atributos HTTP estejam presentes:</p><pre><code>// ValidateRequestMethod checks if the request method is POST.
func ValidateRequestMethod(r *http.Request) error {
	if r.Method != http.MethodPost {
		return fmt.Errorf("method not allowed, only POST is accepted")
	}
	return nil
}

// ValidateRequiredHeaders checks for required GitHub headers.
func ValidateRequiredHeaders(r *http.Request) error {
	eventType := r.Header.Get("X-GitHub-Event")
	deliveryID := r.Header.Get("X-GitHub-Delivery")
	signature := r.Header.Get("X-Hub-Signature-256")
	if eventType == "" || deliveryID == "" || signature == "" {
		return fmt.Errorf("missing required GitHub headers")
	}
	return nil
}

// ValidateUserAgent checks that the User-Agent header starts with GitHub-Hookshot/
func ValidateUserAgent(r *http.Request) error {
	userAgent := r.Header.Get("User-Agent")
	if !strings.HasPrefix(userAgent, "GitHub-Hookshot/") {
		return fmt.Errorf("invalid User-Agent")
	}
	return nil
}</code></pre><p>Embora também valide a assinatura de cada solicitação e da organização.</p><pre><code>// ValidateSignature verifies the GitHub webhook signature.
func ValidateSignature(r *http.Request, secret string) ([]byte, error) {
	payload, err := GitHub.ValidatePayload(r, []byte(secret))
	if err != nil {
		return nil, fmt.Errorf("invalid GitHub signature: %w", err)
	}
	return payload, nil
}

// ValidateAllowedOwner checks if the organization login is in the allowed organizations list.
func ValidateAllowedOwner(payload []byte, allowedGitHubOrganizations []string) (string, error) {
	var orgLogin string
	var payloadMap map[string]any
	if err := json.Unmarshal(payload, &payloadMap); err == nil {
		if orgObj, ok := payloadMap["organization"].(map[string]any); ok {
			if login, ok := orgObj["login"].(string); ok {
				orgLogin = login
			} else if name, ok := orgObj["name"].(string); ok {
				orgLogin = name
			}
		}
	}
	if !slices.Contains(allowedGitHubOrganizations, orgLogin) {
		return orgLogin, fmt.Errorf("organization login not allowed")
	}
	return orgLogin, nil
}</code></pre><p>Por fim, ele direciona para Argo Events com base no tipo de evento:</p><pre><code>	// Map eventType to Argo `EventSource` path
	var endpoint string
	switch eventType {
	case "push":
		endpoint = "/push"
	case "issues":
		endpoint = "/issues"
	case "pull_request":
		endpoint = "/pull-requests"
	default:
		slog.Info("Ignoring unhandled event type", "event_type", eventType, "delivery_id", deliveryID)
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write([]byte("ok"))
		return
	}
	forwardURL := h.config.ArgoEventSourceForwardURL + endpoint</code></pre><p>Do lado da Argo Events, 10 sensores observam o Argo Events EventBus para novos eventos:</p><pre><code>apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
  name: {{ .Values.sensors.packageUpdateOnDefaultBranch.name }}
  namespace: {{ .Release.Namespace }}
spec:
  eventBusName: {{ .Values.eventBus.name }}</code></pre><p>Então, o script aplica a lógica de cada sensor:</p><pre><code>script: |
          local e = event
          if not e or not e.body or not e.body.repository then
            return false
          end

          -- e.g., "refs/heads/main"
          local ref = e.body.ref
          local default_branch = e.body.repository.default_branch
          if not ref or not default_branch then
            return false
          end

          local expected = "refs/heads/" .. default_branch
          if ref ~= expected then
            return false
          end

        {{- if .Values.sensors.packageUpdateOnDefaultBranch.packageFiles }}
          patterns = { {{- range $i, $f := .Values.sensors.packageUpdateOnDefaultBranch.packageFiles }}{{ if $i }}, {{ end }}"{{ $f }}"{{- end }} }
        {{- end }}

          local function anyMatch(path)
            if type(path) ~= "string" then return false end
            for _, pat in ipairs(patterns) do
              -- match filename at repo root, or anywhere under subdirs
              if path:match(pat) or path:match(".+/" .. pat) then
                return true
              end
            end
            return false
          end

          local function filesContainPackage(paths)
            if type(paths) ~= "table" then return false end
            for _, p in ipairs(paths) do
              if anyMatch(p) then return true end
            end
            return false
          end

          -- Inspect all commits (GitHub includes added/modified/removed lists)
          local commits = e.body.commits
          if type(commits) ~= "table" then
            -- Fallback: some payloads include only head_commit
            commits = {}
            if type(e.body.head_commit) == "table" then
              table.insert(commits, e.body.head_commit)
            end
          end

          for _, c in ipairs(commits) do
            if filesContainPackage(c.added) or filesContainPackage(c.modified) or filesContainPackage(c.removed) then
              return true
            end
          end

          return false</code></pre><h3><strong>Sincronizador de Backstage (Go)</strong></h3><p>Este procedimento consulta nosso Catálogo de serviços (Backstage) em busca de Entidades de recursos reais do repositório, transforma-as em CRDs do RepoConfig e mantém a plataforma sincronizada com as alterações de configuração. As alterações são aplicadas em três minutos.</p><pre><code>repoMap := make(map[string]map[string]interface{})
			for i := range entities {
				entity := &entities[i]
				if entity.Spec.Type != "GitHub-repository" {
					continue
				}

				implRaw, err := json.Marshal(entity.Spec.Implementation)
				if err != nil {
					logger.Error("Failed to marshal implementation", "error", err)
					continue
				}

				var implMap map[string]interface{}
				err = json.Unmarshal(implRaw, &implMap)
				if err != nil {
					logger.Error("Failed to unmarshal implementation map", "error", err)
					continue
				}
				var repoName string
				if specMap, ok := implMap["spec"].(map[string]interface{}); ok {
					if repo, ok := specMap["repository"].(string); ok {
						repoName = repo
					}
				}
				if repoName == "" {
					continue
				}

				var workflowsRaw []byte
				if v, ok := implMap["spec"].(map[string]interface{}); ok {
					if r, ok := v["renovate"]; ok {
						workflowsRaw, _ = json.Marshal(r)
					} else {
						workflowsRaw = []byte(`{}`)
					}
				} else {
					workflowsRaw = []byte(`{}`)
				}

				var workflowsWithDefaults schema.WorkflowsMetadata
				err = json.Unmarshal(workflowsRaw, &rworkflowsWithDefaults)
				if err != nil {
					logger.Error("Failed to unmarshal workflows config", "error", err)
					continue
				}

				workflowsMap := map[string]interface{}{
					"enabled":        workflowsWithDefaults.Enabled,
					"require_pr":     workflowsWithDefaults.RequirePr,
					"resource_group": string(workflowsWithDefaults.ResourceGroup),
					"run_frequency":  string(workflowsWithDefaults.RunFrequency),
				}
				repoMap[repoName] = map[string]interface{}{
					"renovate": workflowsMap,
					"owner":    entity.Spec.Owner,
				}
			}
			logger.Info("Fetched GitHub Repository data from Backstage", "repository_count", len(repoMap), "status_code", resp.StatusCode)</code></pre><p>Por fim, ele grava esses dados nas instâncias do RepoConfig.</p><h3><strong>Base de fluxos de trabalho (Misto: JavaScript, Go, Helm)</strong></h3><p>A camada fundamental contém gráficos Helm, configurações JavaScript, um wrapper Go para a CLI do Renovate com suporte a criptografia e um indexador de APK personalizado para pacotes Alpine.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/908d19278face3ce1119dbee9146c1264b6e2f30-1600x873.png" style="width: 100%;" /><h2><strong>Configuração de autoatendimento</strong></h2><p>As equipes configuram os repositórios de forma declarativa através do Backstage:</p><pre><code>spec:
  renovate:
    enabled: true
    config:
      resourceGroup: LARGE      # SMALL | MEDIUM | LARGE  
      runFrequency: "0 */4 * * *"  # Every 4 hours</code></pre><p>Grupos de recursos alocam CPU e memória com base no tamanho do repositório:</p><ul><li><strong>PEQUENA:</strong> CPU de 500m, memória de 1Gi.</li><li><strong>MÉDIA:</strong> CPU de 1000m, memória 2Gi.</li><li><strong>GRANDE:</strong> CPU de 2000 m, memória de 4Gi.</li></ul><p>A configuração é controlada por versão, auditável e aplicada automaticamente.</p><h2><strong>O padrão pai-filho</strong></h2><p>O modelo de execução utiliza um padrão de fluxo de trabalho pai-filho:</p><ul><li><strong>Fluxo de trabalho pai:</strong> CronWorkflow leve executando conforme programado. Criptografa segredos, determina se uma verificação deve ser executada, passa a configuração para o filho.</li><li><strong>Fluxo de trabalho filho:</strong> pod efêmero onde a CLI do Renovate executa. Recursos alocados dinamicamente, descriptografam segredos isoladamente, encerram após a conclusão.</li></ul><p>Essa separação oferece segurança (segredos criptografados no nível dos pais), otimização de recursos (os pais utilizam recursos mínimos) e escalabilidade (os filhos executam em paralelo).</p><h2><strong>Os resultados</strong></h2><h3><strong>Transformação de desempenho</strong></h3><ul><li><strong>Antes:</strong> um repositório por vez, alguns repositórios não seriam processados, possivelmente nem mesmo por um dia ou mais, menos de 1.000 digitalizações por dia.</li><li><strong>Após:</strong> mais de 100 varreduras simultâneas, geralmente 8.000 varreduras e até 10.000 varreduras registradas por dia, limitadas apenas pela quantidade de recursos que estamos dispostos a investir e por como lidamos com os limites de taxa do GitHub.</li></ul><h3><strong>Eficiência de custos</strong></h3><p>Por mais estranho que pareça, rodar 8.000 pods por dia pode te dar o mesmo resultado muito mais barato do que ter um pod de longa duração tentando alcançar os mesmos resultados.</p><p>Na configuração anterior, estávamos executando uma única instância que, em um bom dia, realizaria de 500 a 600 verificações. Ao mesmo tempo, devido ao fato de que diferentes tipos de repositórios seriam executados no mesmo pod, precisávamos dimensionar o pod para os maiores. Esse tamanho seria muito maior do que nossa oferta extra grande atual, usando 8 CPUs para o pod e 16G de memória.</p><p>Para atender à saída diária atual, o pod único precisaria executar por 12 dias. Então, comparando o custo desse único pod funcionando por 12 dias com 8.000 pods do nosso tamanho “MÉDIO” funcionando todos os dias, nosso novo design é muito mais eficiente para a mesma saída de escaneamentos:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Métrica</td><td style="border: 1px solid #ccc; padding: 4px;">Cenário A (Fluxos de trabalho)</td><td style="border: 1px solid #ccc; padding: 4px;">Cenário B (O único pod de longa duração)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Configuração</td><td style="border: 1px solid #ccc; padding: 4px;">8.000 pods (1 vCPU / 2 GB)</td><td style="border: 1px solid #ccc; padding: 4px;">1 pod (8 vCPU / 16 GB)*</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Duração</td><td style="border: 1px solid #ccc; padding: 4px;">10 minutos cada</td><td style="border: 1px solid #ccc; padding: 4px;">12 dias contínuos</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Tempo total de trabalho</td><td style="border: 1px solid #ccc; padding: 4px;">1.333 horas de computação</td><td style="border: 1px solid #ccc; padding: 4px;">288 horas de computação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Custo total</td><td style="border: 1px solid #ccc; padding: 4px;">$ 65,83</td><td style="border: 1px solid #ccc; padding: 4px;">$ 113,75</td>
                    </tr>
              </tbody>
            </table>
          <p>No entanto, vamos levar em consideração que nossa configuração padrão para nossas cargas de trabalho está definida como &quot;PEQUENA&quot;, com a grande maioria funcionando com sucesso com 0,5 CPU e 1 GB de RAM, e apenas algumas precisam ser alteradas para média ou grande. Vamos ver o que acontece se 60% das nossas cargas de trabalho rodarem em &quot;PEQUENA&quot;, 30% em &quot;MÉDIA&quot; e 10% em &quot;GRANDE&quot;, o que está mais próximo da verdade.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Métrica</td><td style="border: 1px solid #ccc; padding: 4px;">Cenário A (Enxame misto)</td><td style="border: 1px solid #ccc; padding: 4px;">Cenário B (O de longa duração)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Estratégia</td><td style="border: 1px solid #ccc; padding: 4px;">8.000 pods (tamanhos variados)</td><td style="border: 1px solid #ccc; padding: 4px;">1 pod (8 vCPU / 16 GB)*</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Duração</td><td style="border: 1px solid #ccc; padding: 4px;">10 minutos cada</td><td style="border: 1px solid #ccc; padding: 4px;">12 dias contínuos</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Custo total</td><td style="border: 1px solid #ccc; padding: 4px;">$ 52,66</td><td style="border: 1px solid #ccc; padding: 4px;">$ 113,75</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Economia</td><td style="border: 1px solid #ccc; padding: 4px;">$ 61,09 (54% mais barato)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
              </tbody>
            </table>
          <p>Podemos ver que, para a mesma saída, somos muito mais econômicos em nosso sistema atual.</p><h3><strong>Segurança aprimorada</strong></h3><ul><li>Tokens efêmeros do GitHub (minutos de exposição versus dias).</li><li>Isolamento de espaço de nome com limites de Controle de acesso por função (RBAC).</li><li>Criptografia de segredos em repouso nos fluxos de trabalho principais.</li><li>Acesso direto ao cofre removido.</li></ul><h3><strong>Desempenho previsível</strong></h3><p>Com frequência de varredura garantida, finalmente podemos definir Objetivos de nível de serviço (SLOs). A automerge funciona de forma confiável. As equipes confiam que a plataforma vai entregar o que é prometido.</p><h2><strong>Principais decisões arquitetônicas</strong></h2><p>Aqui estão algumas das principais decisões de design que moldaram a aparência da plataforma.</p><ul><li><strong>Por que fluxos de trabalho pai-filho?</strong></li></ul><p>Adotamos esse padrão para implementar uma estratégia de <strong>defesa em profundidade</strong>. Ao restringir credenciais de alto valor (como segredos de app GitHub) a um espaço de nome dedicado e bloqueado, utilizamos <strong>RBAC</strong> para garantir que pods de execução efêmeros não possam acessar arbitrariamente dados sensíveis. Vulnerabilidades recentes na cadeia de suprimentos (por exemplo, os ataques de integração contínua/entrega contínua [CI/CD] <strong>&quot;Shai Hulud&quot;</strong>) demonstraram a importância crítica de isolar os ambientes de execução que executam scripts dinâmicos do repositório de credenciais.</p><p>Ao mesmo tempo, essa dissociação permite a <strong>otimização granular de recursos</strong>. Os fluxos de trabalho &quot;pai&quot; atuam como orquestradores leves com um espaço mínimo, enquanto os fluxos de trabalho &quot;filho&quot; lidam com a verificação de dependências com uso intensivo de computação. Essa separação simplifica <strong>gestão de ciclo de vida,</strong> permitindo aplicar uma lógica de reconciliação distinta a cada camada, concedendo aos usuários controle sobre os parâmetros de execução (camada filha) e, ao mesmo tempo, mantendo o controle administrativo sobre a infraestrutura de agendamento e segurança (camada pai).</p><ul><li><strong>Por que é do tipo autoatendimento?</strong></li></ul><p>Eliminar nossa equipe como gargalo para a configuração do repositório era uma exigência crítica. Nossa missão era arquitetar uma <strong>plataforma escalável e de autoatendimento</strong> compatível com diversos casos de uso. Reconhecemos que atuar como <strong>guardiões</strong> de cada alteração de configuração era insustentável, dado o grande volume de repositórios. Em vez disso, adotamos uma filosofia de capacitação: fornecer os &quot;trilhos&quot; (infraestrutura e <strong>proteções</strong>) e capacitar os usuários a conduzir os &quot;trens&quot; (execução e personalização). Acreditamos que essa mudança em direção à <strong>autonomia da equipe</strong> aumenta significativamente a produtividade, permitindo que os usuários adaptem o sistema às suas necessidades operacionais específicas.</p><ul><li><strong>Por que o padrão Operator do Kubernetes?</strong></li></ul><p>Como mencionado acima, um princípio fundamental de design era garantir que a plataforma fosse totalmente <strong>autoatendida</strong>. Precisávamos de um mecanismo automatizado para capturar a intenção do usuário (como alternar varreduras, ajustar a frequência de agendamento ou ajustar limites de recursos em tempo de execução) e propagar instantaneamente essas mudanças para os fluxos de trabalho subjacentes. Antecipando requisitos futuros, o sistema também precisava ser facilmente <strong>extensível</strong>.</p><p>Para alcançar esse objetivo, desenvolvemos um <strong>operador Kubernetes personalizado para gerenciamento de dependências</strong>. Ao usar <strong>CRDs</strong> como interface de configuração, estabelecemos um <strong>ciclo de reconciliação nativo do Kubernetes</strong>. Este operador monitora continuamente o estado desejado definido pelo usuário e orquestra automaticamente as atualizações necessárias na infraestrutura do fluxo de trabalho. Isso garante uma operação perfeita e <strong>orientada a eventos</strong>, onde a lógica da plataforma lida com toda a complexidade nos bastidores.</p><ul><li><strong>Por que projetar um GitHub Events Gateway?</strong></li></ul><p>Adotar uma <strong>arquitetura orientada a eventos (EDA)</strong> foi essencial para a capacidade de resposta da plataforma. Embora os fluxos de trabalho do CronWorkflows fornecessem uma programação de linha de base confiável, precisávamos de agilidade para lidar com <strong>execuções ad hoc, </strong>como usuários acionando varreduras manualmente por meio do dashboard. Para isso, precisávamos de um <strong>gateway de ingestão</strong> dedicado para validar a integridade da carga útil e rotear as solicitações de forma inteligente.</p><p>Avaliamos as soluções existentes, incluindo o EventSource nativo do GitHub para Argo, mas identificamos riscos significativos relacionados à <strong>sobrecarga operacional</strong> e às rígidas <strong>cotas da API do GitHub</strong> (por exemplo, limites de webhook por repositório). Consequentemente, construímos um gateway personalizado para desacoplar nossa infraestrutura dessas limitações.</p><p>Fundamentalmente, esse gateway serviu como um <strong>ponto estratégico de controle de tráfego</strong> durante nossa migração. Ele funcionou como um switch, permitindo que realizássemos uma <strong>implementação gradual e granular</strong> (mudança de tráfego) do sistema legado para a nova infraestrutura. Isso garantiu que a integração de milhares de repositórios fosse um processo controlado e sem riscos, e não uma transição de &quot;big bang&quot;.</p><p></p><h2><strong>Lições aprendidas</strong></h2><p>Algumas lições que aprendemos andam de mãos dadas com o <a href="https://www.elastic.co/about/our-source-code">Elastic Source Code</a>:</p><ol><li><strong>O cliente em primeiro lugar: </strong>as plataformas são criadas para os usuários. Por isso, é importante ter as necessidades dos usuários como prioridade. Isso molda a plataforma em infraestrutura e aplicativos projetados de forma eficiente, que reduzem o atrito com os usuários, simplificam a escalabilidade da plataforma e facilitam a adoção.</li><li><strong>Espaço, tempo: </strong>às vezes, o caminho de menor resistência leva a <strong>areias movediças</strong>. Inicialmente, tentamos otimizar o modelo de processamento sequencial existente, mas isso não resolveu nossos problemas; na verdade, ele apenas introduziu mais complexidade e pontas soltas. A ousada decisão de <strong>reestruturar</strong> a plataforma com processamento paralelo exigiu um esforço inicial significativo. No entanto, isso acabou abrindo caminho para um crescimento sustentável da plataforma e praticamente eliminou o trabalho administrativo diário tedioso.</li><li><strong>TI, depende: </strong>uma plataforma não pode operar isoladamente; o sucesso depende de quão bem ela se integra ao ecossistema mais amplo. Em nosso caso, a integração com o <strong>Backstage</strong> foi fundamental, pois ele serve como a fonte da verdade para a integração perfeita de serviços. Da mesma forma, a conexão com o <strong>Artifactory</strong> nos permitiu gerenciar com eficiência as atualizações de pacotes privados, e a lista de integrações essenciais continua.</li><li><strong>Progresso, perfeição SIMPLES: </strong>durante toda a implementação, testamos constantemente nossas suposições iniciais e nos adaptamos a novas barreiras à medida que elas surgiam. Em vez de ficarmos paralisados pelo perfeccionismo, adotamos uma <strong>abordagem iterativa</strong>, enfrentando desafios um a um e ajustando nossa estratégia migratória para atender às condições do mundo real.</li></ol><h2><strong>O que vem a seguir</strong></h2><p>A entrega da plataforma nos permite realizar trabalhos mais significativos que ajudarão a melhorar a experiência do usuário e a eficiência da nossa plataforma. Alguns exemplos são:<br/></p><ul><li><strong>Aumentar e colocar proteções na adoção do auto-merge</strong></li></ul><p>O recurso de auto-merge acelera significativamente a velocidade da equipe ao eliminar tarefas manuais tediosas. No entanto, precisamos nos certificar de que existam <strong>proteções</strong> rígidas para garantir que esse aumento de velocidade não prejudique a segurança.<br/></p><ul><li><strong>Melhorar a observabilidade da experiência do usuário final</strong></li></ul><p>Uma prioridade crítica para nosso roadmap é aprimorar a observabilidade, não apenas no nível da plataforma, mas também especificamente da <strong>perspectiva do usuário final</strong>. Embora a captura de métricas de infraestrutura seja simples, entender a experiência real do usuário exige insights mais profundos. Estamos trabalhando para definir os indicadores-chave de desempenho centrados no usuário do núcleo (KPIs) para que nossa telemetria possa detectar pontos de atrito e problemas de desempenho <strong>antes que</strong> eles se transformem em reclamações dos usuários.</p><ul><li><strong>Remova obstáculos para a adoção</strong></li></ul><p>Vislumbrando o futuro, nossa prioridade é identificar e remover quaisquer barreiras que dificultem a adoção da plataforma. Seja desenvolvendo novas integrações ou implantando conjuntos específicos de recursos, estamos comprometidos com o planejamento orientado por dados. Criamos uma plataforma projetada para escalabilidade; nosso foco agora se volta para <strong>maximizar o potencial</strong>.<br/></p><h2><strong>O panorama maior</strong></h2><p>O projeto de fluxos de trabalho de gerenciamento de dependências demonstra um princípio mais amplo: <strong>quando você precisa redimensionar ferramentas open source além do modelo de implantação padrão, os padrões nativos do Kubernetes fornecem um caminho a seguir</strong>.</p><p>Ao adotar:</p><ul><li>CRDs para configuração.</li><li>Operadores para gestão de ciclo de vida.</li><li>Arquitetura orientada por eventos para capacidade de resposta</li><li>GitOps para implantação.</li></ul><p>Criamos uma orquestração que se redimensiona independentemente do número de repositórios que gerencia. O desempenho da varredura de um repositório é o mesmo, independentemente de estarmos gerenciando 100 ou 1.000.</p><p>Quando um CVE crítico é anunciado, agora temos respostas em minutos, não em horas. Essa é a diferença entre um gargalo e uma vantagem competitiva.</p><h2><strong>Agradecimentos</strong></h2><p>Esta plataforma utiliza excelentes ferramentas open source:</p><ul><li><strong>Kubebuilder:</strong> o framework open source que usamos para iniciar nossos operadores Kubernetes que inicializam e orquestram nossos fluxos de trabalho. [<a href="https://github.com/kubernetes-sigs/kubebuilder">1</a>][<a href="https://book.kubebuilder.io/">2</a>]</li><li><strong>Backstage:</strong> o open source framework no qual construímos nosso Catálogo de serviços e que usamos como nossa versão final. [<a href="https://github.com/backstage/backstage">1</a>][<a href="https://backstage.io/">2</a>]</li><li><strong>Argo Workflows e Argo Events:</strong> a open source suíte que usamos para orquestrar processos complexos e adicionar processamento dinâmico baseado em eventos. [<span class="unknown__pt__mark__1e5711660421">1</span>][<a href="https://argo-workflows.readthedocs.io/en/stable/">2</a>][<a href="https://argoproj.github.io/argo-events/">3</a>][<a href="https://github.com/argoproj/argo-events">4</a>]</li><li><strong>CLI do Renovate:</strong> a ferramenta de gerenciamento de dependências de open source que processa nossos repositórios. [<a href="https://github.com/renovatebot/renovate">1</a>][<a href="https://docs.renovatebot.com/getting-started/running/">2</a>]</li></ul><p>* O modelo de preços do AWS Fargate foi usado como referência para o custo de um único pod, embora nossas cargas de trabalho não estejam necessariamente sendo executadas na AWS, mas sim em clusters Kubernetes completos.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Melhore o desempenho de busca com "best_compression"]]></title>
            <description><![CDATA[Embora "best_compression" seja normalmente visto como um recurso de economia de armazenamento para casos de uso do Elastic Observability e Elastic Security, este blog demonstra sua eficácia como uma alavanca de ajuste de desempenho para buscas.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/improve-elasticsearch-performance-best-compression</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/improve-elasticsearch-performance-best-compression</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sherry Ger, Ryan Eno]]></dc:creator>
            <pubDate>Fri, 23 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5141a8c2618337207d848ce16b258a86885955b2-1600x1034.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p></p><p>Ao ajustar o Elasticsearch para cargas de trabalho de alta simultaneidade, a abordagem padrão é maximizar a RAM para manter o conjunto de documentos em memória e alcançar baixa latência de busca. Consequentemente, <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules"><code>best_compression</code></a> raramente é considerado para cargas de trabalho de busca, pois é visto principalmente como uma medida de economia de armazenamento para casos de uso do Elastic Observability e Elastic Security, onde a eficiência do armazenamento tem prioridade.</p><p>Neste blog, demonstramos que, quando o tamanho do conjunto de dados excede significativamente o cache de páginas do SO, <code>best_compression</code> melhora o desempenho da busca e a eficiência dos recursos, reduzindo o gargalo de E/S.</p><h2><strong>A configuração</strong></h2><p>Nosso caso de uso é um aplicativo de busca de alta concorrência executado em <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/ec-change-hardware-profile#ec-profiles-compute-optimized-arm">instâncias otimizadas para CPU no Elastic Cloud</a>.</p><ul><li>Volume de dados: ~500 milhões de documentos</li><li>Infraestrutura: 6 instâncias Elastic Cloud (Elasticsearch Service) (cada instância: 1,76 TB de armazenamento | 60 GB de RAM | 31,9 vCPU)</li><li>Relação memória-armazenamento: ~5% do conjunto de dados total cabe na RAM</li></ul><h2><strong>Os sintomas: alta latência</strong></h2><p>Observamos que quando o número de solicitações atuais aumentou drasticamente por volta das 19:00, a latência na busca se deteriorou significativamente. Como mostrado na Figura 1 e na Figura 2, enquanto o tráfego atingiu o pico em torno de 400 solicitações por minuto por instância do Elasticsearch, o tempo médio de serviço de consulta se degradou para mais de 60 ms.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f9c6cc1882e7db24336c65c54bbc1d38dcdb7fa3-697x311.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bbb705ae2fcd14c81d335bf322346caf3bf33765-996x618.png" style="width: 100%;" /><p>O uso da CPU permaneceu relativamente baixo após o tratamento inicial das conexões, indicando que o processamento não era o gargalo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cec15a28d2d22e9adedd2951bb2334b3717890a1-1494x730.png" style="width: 100%;" /><p>Uma forte correlação surgiu entre volume de consultas e falhas de página. À medida que os pedidos aumentavam, observamos um aumento proporcional nas falhas de página, atingindo o pico em torno de 400 mil por minuto. Isso indicava que o conjunto de dados ativo não cabia no cache da página.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/511f2f10300a9d10ba3d7a82b9a8c8d567ac5636-1492x678.png" style="width: 100%;" /><p>Simultaneamente, o uso do heap da JVM parecia normal e saudável. Isso descartou problemas de coleta de lixo e confirmou que o gargalo era de E/S.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/336bbad638f866304358dba1d06ee987de0f23cf-1490x568.png" style="width: 100%;" /><h2><strong>O diagnóstico: limitado por E/S</strong></h2><p>O sistema estava limitado por E/S. <a href="https://www.elastic.co/blog/elasticsearch-caching-deep-dive-boosting-query-speed-one-cache-at-a-time">O Elasticsearch depende do cache de páginas do sistema operacional para fornecer dados de índice a partir da memória</a>. Quando o índice é grande demais para o cache, consultas acionam leituras de disco caras. Embora a solução típica seja redimensionar horizontalmente (adicionar nodes/RAM), queríamos primeiro esgotar as melhorias de eficiência em nossos recursos existentes.</p><h2><strong>A correção</strong></h2><p>Como padrão, o Elasticsearch usa a compressão <a href="https://en.wikipedia.org/wiki/LZ4_(compression_algorithm)">LZ4</a> para seus segmentos de índice, buscando um equilíbrio entre velocidade e tamanho. Hipotetizamos que mudar para <code>best_compression</code> (que usa <a href="https://en.wikipedia.org/wiki/Zstd">zstd</a>) reduziria o tamanho dos índices. Um espaço menor permite que uma porcentagem maior do índice caiba no cache da página, trocando um aumento insignificante na CPU (por descompressão) por uma redução na E/S do disco.</p><p>Para habilitar <code>best_compression</code>, reindexamos os dados com a configuração de índice <code>index.codec: best_compression</code>. O mesmo resultado poderia ser alcançado fechando o índice, resetando o codec do índice para <code>best_compression</code>, e então realizando uma fusão de segmentos.</p><pre><code>POST my-index/_close
PUT my-index/_settings
{
    "codec": "best_compression"
}
  
POST my-index/_open  
POST my-index/_forcemerge?max_num_segments=1</code></pre><h2><strong>Os resultados</strong></h2><p>Os resultados confirmaram nossa hipótese: a eficiência aprimorada do armazenamento se traduziu diretamente em um aumento substancial no desempenho da busca, sem nenhum aumento na utilização da CPU.</p><p>A aplicação de <code>best_compression</code> reduziu o tamanho do índice em aproximadamente 25%. Embora menor do que a redução observada em dados de log repetitivos, essa redução de 25% aumentou efetivamente nossa capacidade de cache de páginas pela mesma margem.</p><p>Durante o próximo teste de carga (começando às 17:00), o tráfego foi ainda maior, atingindo o pico de 500 solicitações por minuto por nó Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc1902f396cb2115c0013155ad07f6eb87389c60-660x309.png" style="width: 100%;" /><p>Apesar da maior carga, a utilização da CPU foi menor do que na execução anterior. O uso elevado no teste anterior provavelmente se deveu à sobrecarga do tratamento excessivo de falhas de página e gerenciamento de E/S de disco.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/15699ef4c65b3f0a9f8a3e1bae8bb18f7b647025-819x352.png" style="width: 100%;" /><p>Crucialmente, as falhas de página caíram significativamente. Mesmo em maior débito, as falhas ficaram em torno de &lt;200 mil por minuto, comparado a &gt;300 mil no teste inicial.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f76ca967976d740af88a9359b66041701abb46fc-764x340.png" style="width: 100%;" /><p>Embora os resultados de falha na página ainda não tenham sido ideais, o tempo de serviço de consulta foi reduzido em cerca de 50%, pairando abaixo de 30 ms, mesmo sob carga mais pesada.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/750ec1c59b8eb5069aed4c066d856ecea82d5bca-620x311.png" style="width: 100%;" /><p></p><h2><strong>A conclusão: best_compression para busca</strong></h2><p>Para casos de uso de busca em que o volume de dados excede a memória física disponível, <code>best_compression</code> é uma poderosa alavanca de ajuste de desempenho.</p><p>A solução convencional para falhas de cache é redimensionar para aumentar a RAM. No entanto, ao reduzir a pegada do índice, alcançamos o mesmo objetivo: maximizar a contagem de documentos no cache da página. Nosso próximo passo é explorar <a href="https://www.elastic.co/blog/space-savings-a-lesser-known-benefit-of-index-sorting-in-elasticsearch"><strong>a classificação de índices</strong></a> para otimizar ainda mais o armazenamento e extrair ainda mais desempenho de nossos recursos existentes.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Agent Builder agora em GA: envie agentes orientados por contexto em questão de minutos]]></title>
            <description><![CDATA[O Agent Builder agora está disponível na versão GA. Saiba como isso permite que você desenvolva agentes de IA orientados por contexto.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agent-builder-elastic-ga</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agent-builder-elastic-ga</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Anish Mathur, Evan Castle]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6840eb7dbb884055ab0e965dcfd614fec54936af-2210x1440.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>É com grande satisfação que anunciamos a disponibilidade geral do Agent Builder no Elastic Cloud Serverless e na próxima versão 9.3. O Agent Builder utiliza o poder do Elasticsearch como uma plataforma de engenharia de contexto para desenvolver de forma rápida agentes de IA contextuais e focados em dados.</p><p>Os agentes estão ganhando força, impulsionados pelo potencial de entregar ganhos de eficiência e melhores experiências para os clientes. Mas, na prática, fornecer aos agentes o contexto correto é difícil, principalmente quando se trabalha com dados empresariais desorganizados e não estruturados. Os desenvolvedores precisam gerenciar ferramentas, prompts, estado, lógica de raciocínio, modelos e, principalmente, recuperar o contexto relevante das fontes comerciais para fornecer resultados e ações precisos. O Elastic Agent Builder oferece esses componentes essenciais para desenvolver agentes seguros, confiáveis e orientados ao contexto.</p><h2>Principais funcionalidades do Agent Builder</h2><p>O Agent Builder aproveita os investimentos de longo prazo da Elastic na relevância de busca e retrieval-augmented generation, e trabalha para tornar o Elasticsearch o melhor banco de dados vetorial para simplificar o desenvolvimento de agentes de IA contextuais e focados em dados.</p><p>O Agent Builder permite que você:</p><ul><li>Comece com um agente conversacional integrado que pode responder a perguntas, realizar análises e conduzir investigações sobre quaisquer dados no Elasticsearch.</li><li>Passe de dados complexos não estruturados para um agente personalizado com uma experiência de desenvolvimento baseada em configuração.</li><li>Aproveite a relevância de pesquisa híbrida de ponta por meio do ES|QL integrado ou de ferramentas personalizadas para melhorar a qualidade do contexto e a confiabilidade do agente.</li><li>Execute fluxos de trabalho complexos (pré-visualização) como ferramentas reutilizáveis para enriquecer dados, atualizar registros, enviar mensagens e muito mais para automação baseada em regras.</li><li>Conecte-se a fontes de dados fora do Elasticsearch usando fluxos de trabalho e MCP para correlacionar e combinar o contexto dos agentes.</li><li>Integre-se a qualquer framework agêntico ou aplicação usando ferramentas integradas e personalizadas expostas via MCP, além da capacidade de conectar-se a MCPs externos (em pré-visualização), suporte para A2A e suporte completo à API.</li><li>Amplie os recursos do Agent Builder com integração a soluções de terceiros, como o LlamaIndex para processamento complexo de documentos ou o Arcade.dev para acesso seguro e estruturado a ferramentas.</li></ul><p>Para ampliar ainda mais a funcionalidade do Agent Builder, apresentamos o Elastic Workflows, nossos novos recursos de automação baseados em regras, agora em versão prévia técnica. Para tarefas organizacionais, os agentes às vezes precisam de certeza e confiabilidade de ações baseadas em regras, que geralmente são necessárias para implementar uma lógica comercial específica. O Elastic Workflows oferece aos agentes uma maneira simples e declarativa de orquestrar sistemas internos e externos para executar ações, coletar e transformar dados e contexto. Os fluxos de trabalho são totalmente componíveis, orientados a eventos e flexíveis, e podem ser expostos como ferramentas a um agente via MCP.</p><h2>De dados a agentes em questão de minutos</h2><p>Os agentes de desenvolvimento podem levar semanas de trabalho inicial para consolidar armazenamentos de dados separados, criar pipelines manuais, ajustar consultas e gerenciar orquestrações complexas. O Agent Builder reduz o tempo de desenvolvimento para os agentes, acabando com a necessidade de armazenamentos de dados separados, bancos de dados vetoriais, pipelines RAG, camadas de pesquisa, tradutores de consultas e orquestradores de ferramentas, permitindo que você se concentre na lógica do agente e na entrega do aplicativo.</p><p>O Agent Builder integra de forma nativa primitivas da plataforma Elasticsearch para agilizar o desenvolvimento de agentes.</p><ul><li>Comece com um agente conversacional integrado que pode conversar e raciocinar imediatamente com seus dados indexados.</li><li>Integre agentes em aplicações, dashboards ou sistemas de CI/CD com acesso interativo via Kibana, APIs ou MCP e A2A.</li><li>Crie com as ferramentas padrão para entender a estrutura dos seus dados, selecionar o índice apropriado, gerar consultas híbridas, semânticas e estruturadas otimizadas e criar visualizações configuráveis usando ES|QL com base em comandos em linguagem natural.</li></ul><p>Para se aprofundar, veja um <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">passo a passo prático</a> e completo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b55b63eae40f72952967cc8f3ea4df4cd62d7d70-1080x608.gif" style="width: 100%;" /><h2>Crie com o Elasticsearch, uma plataforma de dados completa para engenharia de contexto</h2><p>Para agentes de IA, a qualidade do contexto é essencial para fornecer raciocínio eficaz e reduzir os riscos de alucinação. Para muitos agentes de IA corporativa, os dados de negócios necessários para realizar uma tarefa são a peça fundamental de contexto. Como um armazenamento de dados altamente escalável, banco de dados vetorial e líder em relevância, o Elasticsearch já oferece muitas primitivas fortes de engenharia de contexto. A engenharia de contexto vai além da simples retrieval-augmented generation, permitindo que você personalize e redimensione como os dados são obtidos, ranqueados, filtrados e apresentados aos agentes, ajudando a reduzir ruído e ambiguidade.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/419b9b6f13739e0a8983249d8ac31478e73dac89-1600x901.png" style="width: 100%;" /><p>O Elasticsearch oferece um mecanismo de contexto que combina busca lexical, busca vetorial e filtragem estruturada para recuperação de dados, o que <a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">melhora o desempenho do LLM</a> ao garantir que o modelo opere em um contexto relevante e preciso. Essa capacidade é suportada por recuperação agêntica, juntamente com ferramentas integradas e lógica de busca que selecionam automaticamente os índices corretos e transformam a linguagem natural em consultas otimizadas para o contexto.</p><p>Com o Agent Builder, você garante que os agentes recebam primeiro o contexto mais útil com controles de relevância e classificação, permitindo que você ajuste a lógica de pontuação, classificação e filtragem. O Elasticsearch permite que você controle o que importa, por que importa e como é priorizado, em vez de depender de um comportamento opaco de recuperação. Tudo isso é sustentado pelo Elasticsearch como uma plataforma de dados escalável para armazenar e escalar todos os seus dados de texto, vetores, metadados, logs e muito mais em uma plataforma, facilitando o gerenciamento do contexto para os agentes.</p><h2>Executar fluxos de trabalho complexos como ferramentas reutilizáveis</h2><p>Enquanto agentes de IA permitem o raciocínio para tarefas complexas, grande parte da automação depende da execução confiável de ações baseadas em regras que aplicam lógica de negócios específica. O Elastic Workflows oferece uma maneira simples e declarativa de orquestrar sistemas internos e externos para realizar ações, coletar contexto ou dados e integrá-los como parte dos agentes. Definidos em YAML, os fluxos de trabalho são totalmente componíveis, permitindo que sejam tão simples ou complexos quanto o trabalho exigir. Isso oferece aos agentes uma maneira eficiente de agir em toda a plataforma e nas soluções do Elasticsearch, bem como com aplicativos de terceiros.</p><p>A integração de um fluxo de trabalho com o Agent Builder pode ser feita em três etapas (pré-requisito: habilitar fluxos de trabalho com detalhes fornecidos <a href="https://github.com/elastic/workflows">aqui</a>)</p><p>1. Criar e salvar um novo fluxo de trabalho usando o editor simples baseado em YAML com autopreenchimento e testes integrados.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/308888bf3d2fa013f9391a55be6a6fbd458b6dac-1600x998.png" style="width: 100%;" /><p>2. Crie uma nova ferramenta no Agent Builder com o tipo “Fluxo de trabalho” e informe uma descrição para ajudar o agente a determinar quando usar a ferramenta de fluxo de trabalho.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c04810d30d226112c3610bd58e208607b213fc3d-1600x945.png" style="width: 100%;" /><p>3. Adicione a ferramenta de fluxo de trabalho ao seu agente personalizado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/724cd4ac93c46efb0d339fd140e5caf138f8150f-1600x948.png" style="width: 100%;" /><p>4. É isso aí! Agora o agente pode chamar o fluxo de trabalho dentro de uma conversa.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8dfdd726ab89e31c48b79372650ce33946713dca-1600x929.png" style="width: 100%;" /><h2>Seu agente, suas regras</h2><p>O Agent Builder não te prende a um único paradigma de desenvolvimento. Em vez disso, ele foi projetado para permitir abordagens de desenvolvimento abertas e flexíveis para agentes com controle total de dados, relevância, modelos, interoperabilidade, security e design de agentes.</p><p>As definições de agentes personalizados permitem que você escolha exatamente quais ferramentas um agente pode acessar, incorpore avisos de sistema personalizados, adapte as instruções do agente e defina limites de segurança. Os agentes permanecem independentes do modelo, permitindo que você configure com flexibilidade um LLM preferido, tanto nativo quanto em todo o ecossistema, sem ficar preso a um único provedor.</p><p>Crie ferramentas extensíveis que encapsulem lógica específica do domínio (por exemplo, filtros de índice específicos, junções ES|QL, pipelines analíticos) e restrinja-as para uso seguro em produção. O suporte completo à API permite a interoperabilidade com outras frameworks de agentes, com suporte nativo ao Protocolo de Contexto do Modelo (MCP). A integração A2A significa que você pode expor seus agentes Elastic a outros frameworks, serviços e apps clientes, reutilizando a mesma lógica de engenharia de dados e contexto em todas as integrações.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5e903ba24ffb3f40231e901f63bd494c89cb7757-1600x1004.png" style="width: 100%;" /><p>O Agent Builder suporta desenvolvimento flexível e aberto e foi projetado para se integrar com frameworks e plataformas populares de agentes. Essas integrações podem ser essenciais para entregar agentes eficazes. Como descreve <strong>Sam Partee, cofundador da Arcade.dev</strong>,</p><p><em>&quot;Sistemas agênticos falham hoje porque conectar IA a ferramentas e dados é algo complexo. O Elastic Agent Builder com Arcade.dev oferece aos desenvolvedores uma maneira estruturada e segura de lidar com a forma como os agentes recuperam o contexto, raciocinam e agem, levando os agentes da demonstração ao nível de produção.&quot;</em></p><p>O Agent Builder também aproveita a extensibilidade do Elasticsearch para lidar com dados complexos. Como descreve <strong>Jerry Liu, CEO da LlamaIndex </strong>,</p><p><em>“Extrair o contexto empresarial de fontes de dados não estruturadas é fundamental para a criação de agentes eficazes. O Elastic Agent Builder combinado com o processamento de documentos complexos do LlamaIndex fortalece a camada fundamental de contexto, ajudando as equipes a recuperar, processar e preparar dados para que os agentes possam raciocinar com mais precisão e oferecer melhores resultados.&quot;</em></p><h2>O que você pode construir?</h2><p>O Agent Builder já está sendo usado para vários casos de uso. Abaixo estão alguns exemplos e arquiteturas de referência para começar a usar agentes:</p><ul><li><strong>Automatizar infraestrutura: </strong>em cenários de suporte, os agentes têm sido usados para ler, pensar e conversar, mas até o momento, eles não conseguem acessar e tocar a infraestrutura que possam precisar gerenciar. A equipe de engenharia da Elastic criou um agente para <a href="https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure">gerenciamento automatizado de infraestrutura</a> como parte de um hackathon. O agente investiga ativamente problemas com a infraestrutura da aplicação e age de forma automatizada. Ele usa fluxos de trabalho para otimizar configurações, responder a problemas e redimensionar recursos, tudo com base em uma compreensão inteligente dos logs de infraestrutura.</li><li><strong>Análise de ameaças à segurança: </strong>um agente de vulnerabilidade de segurança foi desenvolvido com Elastic Agent Builder, MCP e Elasticsearch. Ele automatiza a análise de ameaças correlacionando dados de segurança internos com inteligência de ameaças externa. O agente realiza buscas semânticas em incidentes e configurações históricas, amplia os resultados com dados da internet em tempo real e aplica o raciocínio LLM para avaliar a relevância ambiental, priorizar riscos e produzir medidas corretivas viáveis. Consulte a <a href="https://www.elastic.co/search-labs/blog/agent-builder-mcp-reference-architecture-elasticsearch">arquitetura de referência</a><strong>.</strong></li><li><strong>Suporte técnico ao cliente: </strong>os agentes podem executar diversas tarefas de suporte, incluindo resumo de casos, identificação e criação de problemas duplicados e investigação técnica aprofundada. O Agent Builder permite isso com uma pesquisa híbrida de várias etapas para encontrar somente os problemas, soluções e procedimentos relacionados mais relevantes e formular hipóteses de causa raiz e planos de remediação. O Agent Builder pode simplificar a arquitetura de <a href="https://www.elastic.co/blog/generative-ai-customer-support-elastic-support-assistant">sistemas de suporte</a> complexos e acelerar o tempo de entrega.</li><li><strong>Descoberta de produtos e conteúdo:</strong> o Agent Builder simplifica o processo de <a href="https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder">expor catálogos complexos de produtos para experiências conversacionais</a>, ao mesmo tempo em que permite que as organizações mantenham flexibilidade para incluir a própria lógica e requisitos de negócios.</li><li><strong>Crie você mesmo:</strong> participe do <a href="https://elasticsearch.devpost.com/">Hackathon do Agent Builder,</a> que ocorrerá de 22 de janeiro a 27 de fevereiro de 2026. Trabalhe com a comunidade para criar agentes de IA orientados por contexto e em várias etapas que combinem buscar, fluxos de trabalho, ferramentas e raciocínio para automatizar tarefas do mundo real*</li></ul><h2>Comece a criar agentes personalizados agora</h2><p>Comece com um <a href="https://cloud.elastic.co/registration?onboarding_token=search&amp;pg=en-enterprise-search-page">teste do Elastic Cloud</a> e confira a documentação <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">aqui</a>. Para clientes existentes, o Agent Builder está disponível no Cloud Serverless e no nível Empresarial no Elastic Cloud Hosted e autogerenciado.</p><p>* <a href="https://elasticsearch.devpost.com/rules">Clique aqui</a> para ver os termos, condições e requisitos de elegibilidade para o hackathon</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo agentes de voz com o Elastic Agent Builder]]></title>
            <description><![CDATA[Explorando como funcionam os agentes de voz e como construir um usando o Elastic Agent Builder e o LiveKit.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/build-voice-agents-elastic-agent-builder</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/build-voice-agents-elastic-agent-builder</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kenneth Kreindler]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/43ceabb9e2c0966261c188bd40e03178d5a91e5c-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A IA ficou presa em uma caixa de vidro. Você digita comandos, ela responde com texto, e pronto. É útil, mas distante, como ver alguém se mover atrás de uma tela. Este ano, 2026, será o ano em que as empresas vão quebrar esse vidro e trazer agentes de IA para produtos, onde realmente entregam valor.</p><p>Uma das maneiras pelas quais o vidro será quebrado é pela adoção de <em>agentes de voz</em>, que são agentes de IA que reconhecem a fala humana e sintetizam áudio gerado por computador. Com o crescimento das transcrições de baixa latência, modelos de linguagem grandes e rápidos (LLMs) e modelos de texto para fala que soam humanos, isso se tornou possível.</p><p>Os agentes de voz também precisam ter acesso a dados empresariais para se tornarem realmente valiosos. Neste artigo, aprenderemos como funcionam os agentes de voz e criaremos um para a ElasticSport, uma loja fictícia de equipamentos esportivos para atividades ao ar livre, usando <a href="https://livekit.io/">LiveKit</a> e <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>. Nosso agente de voz será sensível ao contexto e funcionará com nossos dados.</p><h2>Como funciona</h2><p>Existem dois paradigmas no mundo dos agentes de voz: o primeiro usa modelos de fala para fala, e o segundo usa um pipeline de voz que consiste em fala para texto, LLM e texto para fala. Modelos de fala para fala têm os próprios benefícios, mas os pipelines de voz oferecem muito mais personalização das tecnologias usadas e de como o contexto é gerenciado, além de controle sobre o comportamento do agente. Vamos focar o modelo de pipeline de voz.</p><h3>Principais componentes</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b237501903f9c3a71fe1b7755c3990e40c5495c8-1600x653.png" style="width: 100%;" /><h4>Transcrição (fala para texto)</h4><p>A transcrição é o ponto de entrada no pipeline de voz. O componente de transcrição recebe como entrada quadros de áudio brutos, transcreve a fala em texto e gera esse texto como saída. O texto transcrito é armazenado em buffer até que o sistema detecte que a fala do usuário terminou, momento em que a geração do LLM é iniciada. Diversos fornecedores terceirizados oferecem transcrições de baixa latência. Ao selecionar um, leve em consideração a latência e a precisão da transcrição, e certifique-se de que ele suporte transcrições em fluxo contínuo.</p><p></p><p>Exemplos de APIs de terceiros: <a href="https://www.assemblyai.com/">AssemblyAI</a>, <a href="https://deepgram.com/product/speech-to-text">Deepgram</a>, <a href="https://platform.openai.com/docs/guides/realtime-transcription">OpenAI</a>, <a href="https://elevenlabs.io/speech-to-text">ElevenLabs</a></p><h4>Detecção de curva</h4><p>A detecção de turno é o componente do pipeline que detecta quando o falante termina de falar e a geração deve começar. Uma maneira comum de fazer isso é por meio de um modelo de detecção de atividade de voz (VAD), como o <a href="https://github.com/snakers4/silero-vad">Silero VAD</a>. O VAD utiliza níveis de energia do áudio para detectar quando o áudio contém fala e quando a fala terminou. No entanto, o VAD sozinho não consegue identificar a diferença entre pausa e fim da fala. Por isso, muitas vezes é combinado com um modelo de fim de enunciado que prevê se o falante terminou de falar, com base na transcrição intermediária ou no áudio bruto.</p><p>Exemplos (Hugging Face): <a href="https://huggingface.co/livekit/turn-detector">livekit/turn-detector</a>, <a href="https://huggingface.co/pipecat-ai/smart-turn-v3">pipecat-ai/smart-turn-v3</a></p><h4>Agente</h4><p>O agente é o núcleo de um pipeline de voz. É responsável por entender a intenção, reunir o contexto certo e formular uma resposta em formato de texto. <a href="https://www.elastic.co/elasticsearch/agent-builder">O Elastic Agent Builder</a>, com suas capacidades integradas de raciocínio, biblioteca de ferramentas e integração de fluxos de trabalho, é um agente capaz de trabalhar sobre seus dados e interagir com serviços externos.</p><h4>LLM (texto para texto)</h4><p>Ao selecionar um LLM para o Elastic Agent Builder, há duas características principais a considerar: benchmarks de raciocínio de LLM e tempo até o primeiro token (TTFT).</p><p>Benchmarks de raciocínio indicam quão bem o LLM consegue gerar respostas corretas. Os benchmarks a serem considerados são aqueles que avaliam a adesão à conversa em múltiplos turnos e os benchmarks de inteligência, como o MT-Bench e o conjunto de dados Humanity&#x27;s Last Exam, respectivamente.</p><p>Os benchmarks TTFT avaliam a rapidez com que o modelo produz seu primeiro token de saída. Existem outros tipos de benchmarks de latência, mas a TTFT é particularmente importante para agentes de voz, pois a síntese de áudio pode começar assim que o primeiro token é recebido, resultando em menor latência entre os turnos, uma conversa com sensação natural.</p><p>Normalmente, é preciso fazer uma troca entre essas duas características porque modelos mais rápidos geralmente têm um desempenho pior em benchmarks de raciocínio.</p><p>Exemplos (Hugging Face): <a href="https://huggingface.co/openai/gpt-oss-20b">openai/gpt-oss-20b</a>, <a href="https://huggingface.co/openai/gpt-oss-120b">openai/gpt-oss-120b</a></p><h4>Síntese (texto para fala)</h4><p>A parte final do pipeline é o modelo de conversão de texto em fala. Esse componente é responsável por converter a saída de texto do LLM em fala audível. Semelhante ao LLM, a latência é uma característica a ser observada ao selecionar um provedor de texto para fala. A latência de texto para fala é medida pelo tempo até o primeiro byte (TTFB). Esse é o tempo que leva para receber o primeiro byte de áudio. Menor TTFB também reduz a latência de giro.</p><p>Exemplos: <a href="https://elevenlabs.io/text-to-speech-api">ElevenLabs</a>, <a href="https://cartesia.ai/sonic">Cartesia</a>, <a href="https://www.rime.ai/">Rime</a></p><h4>Construção do pipeline de voz</h4><p>O Elastic Agent Builder pode ser integrado a um pipeline de voz em vários níveis diferentes:</p><ol><li>Ferramentas apenas do Agent Builder: fala para texto → LLM (com ferramentas Agent Builder) → texto para fala</li><li>Agent Builder como MCP: fala para texto → LLM (com acesso ao Agent Builder via MCP) → texto para fala</li><li>Agent Builder como núcleo: conversão de fala em texto → Agent Builder → conversão de texto em fala</li></ol><p>Para este projeto, escolhi o Agent Builder como abordagem núcleo. Com essa abordagem, toda a funcionalidade do Agent Builder e dos fluxos de trabalho pode ser utilizada. O projeto usa o LiveKit para orquestrar a conversão de fala em texto, detecção de turnos e conversão de texto em fala, e implementa um node LLM personalizado que se integra diretamente ao Agent Builder.</p><h2>Agente de voz do suporte da Elastic</h2><p>Criaremos um agente de voz de suporte personalizado para uma loja de esportes fictícia chamada ElasticSport. Os clientes poderão ligar para a linha de ajuda, solicitar recomendações de produtos, encontrar detalhes do produto, verificar o status do pedido e receber as informações do pedido por mensagem de texto. Para isso, primeiro precisamos configurar um agente personalizado e criar ferramentas para executar a Elasticsearch linguagem de consulta (ES|QL) e fluxos de trabalho.</p><a href="https://videos.elastic.co/watch/jpX6D21URZuh3PtXVV7Ks6">Watch video on Vidyard</a><h3>Configurando o agente</h3><h4>Prompt</h4><p>O prompt orienta o agente qual a personalidade que deve ter e como responder. É importante ressaltar que há alguns prompts específicos de voz que garantem que as respostas sejam sintetizadas em áudio adequadamente e que os mal-entendidos sejam recuperados de forma graciosa.</p><pre><code>You are a Sales Assistant at ElasticSport, an outdoor sport shop specialized in hiking and winter equipment. 

[Profile]
- name: Iva
- company: ElasticSport
- role: Sales Assistant
- language: en-GB
- description: ElasticSport virtual sales assistant

[Context]
- Ask clarifying questions to understand the context.
- Use available tools to answer the user's question.
- Use the knowledge base to retrieve general information

[Style]
- Be informative and comprehensive.
- Maintain a professional, friendly and polite tone.
- Mimic human behavior and speech patterns.
- Be concise. Do not over explain initially

[Response Guideline]
- Present dates in spelled-out month date format (e.g., January fifteenth, two thousand and twenty-four).
- Avoid the use of unpronounceable punctuation such as bullet points, tables, emojis.
- Respond in plain text, avoid any formatting.
- Spell out numbers as words for more natural-sounding speech.
- Respond in short and concise sentences. Responses should be 1 or 2 sentences long.

[ERROR RECOVERY]
### Misunderstanding Protocol
1. Acknowledge potential misunderstanding
2. Request specific clarification</code></pre><h4>Fluxos de trabalho</h4><p>Vamos adicionar um pequeno fluxo de trabalho para enviar um SMS pela API de mensagens do Twilio. O fluxo de trabalho será exposto ao agente personalizado como uma ferramenta, resultando em uma experiência de usuário em que o agente poderá enviar um SMS ao chamador durante a chamada. Isso permite que o autor da chamada, por exemplo, pergunte: &quot;Você pode enviar os detalhes sobre <em>X</em> por texto?&quot;</p><pre><code>name: send sms
enabled: true
triggers:
  - type: manual
inputs:
  - name: message
    type: string
    description: The message to send to the phone number.

  - name: phone_number
    type: string
    description: The phone number to send the message to.

consts:
  TWILIO_ACCOUNT: "****"
  BASIC_AUTH: "****"
  FROM_PHONE_NNUMBER: "****"
steps:
  - name: http_step
    type: http
    with:
      url: https://api.twilio.com/2010-04-01/Accounts/{{consts.TWILIO_ACCOUNT}}/Messages.json
      method: POST
      headers:
        Content-Type: application/x-www-form-urlencoded
        Authorization: Basic {{consts.BASIC_AUTH | base64_encode}}
      body: From={{consts.FROM_PHONE_NNUMBER}}&To={{inputs.phone_number}}&Body={{inputs.message}}
      timeout: 30s</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b057e71b0a7c50eb3da47cd4f95e77ec7b4c6126-1600x1245.png" style="width: 100%;" /><h4>Ferramentas ES|QL</h4><p>As ferramentas a seguir permitem que o agente forneça respostas relevantes fundamentadas em dados reais. O repositório de exemplo contém um script de configuração para iniciar o Kibana com conjuntos de dados de produtos, pedidos e base de conhecimento.</p><ul><li><strong>Product.search</strong></li></ul><p>O conjunto de dados de produtos contém 65 produtos fictícios. Este é um documento de exemplo:</p><pre><code>{
      "sku": "ort3M7k",
      "name": "Ortovox Free Rider 26 Backpack",
      "price": 189,
      "currency": "USD",
      "image": "https://via.placeholder.com/150",
      "description": "The Ortovox Free Rider 26 is a technical freeride backpack with a dedicated safety compartment and diagonal ski carry system. Perfect for backcountry missions.\n\nKey Features:\n- 26L capacity\n- Diagonal ski carry system\n- Safety equipment compartment\n- Helmet holder\n- Hydration system compatible",
      "category": "Accessories",
      "subCategory": "Backpacks",
      "brand": "Ortovox",
      "sizes": ["One Size"],
      "colors": ["Black", "Blue", "Orange"],
      "materials": ["Nylon", "Polyester"]
    }</code></pre><p>Os campos de nome e descrição são mapeados como <code>semantic_text</code>, permitindo que o LLM use busca semântica via ES|QL para recuperar produtos relevantes. A consulta de busca híbrida realiza correspondência semântica em ambos os campos, com um peso um pouco maior aplicado às correspondências no campo do nome usando um boost.</p><p>A consulta primeiro recupera os 20 melhores resultados classificados pela pontuação inicial de relevância. Esses resultados são então reclassificados com base no campo de descrição usando o modelo de inferência <code>.rerank-v1-elasticsearch</code> e, finalmente, reduzidos para os cinco produtos mais relevantes.</p><pre><code>type: ES|QL
toolId: products.search
description: Use this tool to search through the product catalogue by keywords.
query: |
    FROM products
        METADATA _score
      | WHERE
          MATCH(name, ?query, {"boost": 0.6}) OR
            MATCH(description, ?query, {"boost": 0.4})
      | SORT _score DESC
      | LIMIT 20
      | RERANK ?query
            ON description
            WITH {"inference_id": ".rerank-v1-elasticsearch"}
      | LIMIT 5

parameters:
    query: space separated keywords to search for in catalogue</code></pre><ul><li><strong>Knowledgebase.search</strong></li></ul><p>Os conjuntos de dados da base de conhecimento contêm documentos do seguinte formato, onde os campos de título e conteúdo são armazenados como texto semântico:</p><pre><code>{
        id: "8273645",
        createdAt: "2025-11-14",
        title: "International Orders",
        content: `International orders are processed through our international shipping partner. Below are the countries we ship to and average delivery times.
        Germany: 3-5 working days
        France: 3-5 working days
        Italy: 3-5 working days
        Spain: 3-5 working days
        United Kingdom: 3-5 working days
        United States: 3-5 working days
        Canada: 3-5 working days
        Australia: 3-5 working days
        New Zealand: 3-5 working days
        `
}</code></pre><p>E a ferramenta usa uma consulta semelhante à ferramenta<code>product.search</code>:</p><pre><code>type: "ES|QL"
toolId: knowledgebase.search
description: Use this tool to search the knowledgebase.
query: |
  FROM knowledge_base
    METADATA _score
  | WHERE
      MATCH(title, ?query, {"boost": 0.6}) OR
      MATCH(content, ?query, {"boost": 0.4})
  | SORT _score DESC
  | LIMIT 20
  | RERANK ?query
      ON content
      WITH {"inference_id": ".rerank-v1-elasticsearch"}
  | LIMIT 5

parameters:
  query: space separated keywords or natural language phrase to semantically search for in the knowledge base</code></pre><ul><li><strong>Orders.search</strong></li></ul><p>A última ferramenta que adicionaremos é aquela usada para recuperar pedidos por <code>order_id</code>:</p><pre><code>type: "ES|QL"
toolId: order.search
description: Use this tool to retrieve an order by its ID.
query: |
  FROM orders
    METADATA _score
  | WHERE order_id == ?order_id
  | SORT _score DESC
  | LIMIT 1

parameters:
  order_id: "the ID of the order"</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d22bdd540a95b5a9c2bd5f308620835e8e6f7ecb-1600x1361.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d91709a50fb5391876b714885242d998b2b21027-1600x1443.png" style="width: 100%;" /><p>Após configurar o agente e anexar esses fluxos de trabalho e ES|QL para o agente, o agente pode ser testado dentro do Kibana.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8691f41624247a6b1352d158c970031e1426ce5e-1600x1056.png" style="width: 100%;" /><p>Além de construir um agente de suporte ElasticSport, o agente, fluxos de trabalho e ferramentas podem ser adaptados a outros casos de uso, como um agente de vendas que qualifica leads, um agente de serviço para reparos residenciais, reservas para um restaurante ou um agente de agendamento de consultas.</p><p></p><p>A parte final é conectar o agente que acabamos de criar com modelos LiveKit, texto para fala e fala para texto. O repositório (link no fim do artigo) contém um node personalizado do LLM Elastic Agent Builder que pode ser usado com o LiveKit. Basta trocar o <code>AGENT_ID</code> pelo seu e vinculá-lo com sua instância Kibana.</p><h2>Para começar</h2><p>Confira o código e experimente <a href="https://github.com/KDKHD/elastic_agent_builder_livekit">você mesmo aqui</a>. </p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construtor de agentes além da caixa de bate-papo: apresentando a infraestrutura ampliada]]></title>
            <description><![CDATA[Saiba mais sobre o Elastic Agent Builder com infraestrutura ampliada, um agente de IA que permite operações ampliadas, desenvolvimento aprimorado e synthetics ampliados.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agent-builder-augmented-infrastructure</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agent-builder-augmented-infrastructure</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Wert, Bill Easton, Gil Raphaelli, Graham Hudgins, Norrie Taylor]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e08daf78f328e826f39d06329f6a5487f75d178d-1272x700.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>Nós não falamos. Nós fazemos.</strong></p><p>Todos nós já vimos o crescimento dos agentes de IA. Eles são fantásticos em resumir textos, escrever trechos de código e responder perguntas baseadas em documentação. Mas, para nós que trabalhamos com DevOps e engenharia de confiabilidade de sites (SRE), houve uma limitação frustrante. A maioria dos agentes está presa ao paradigma do call center, o que significa que eles podem ler, pensar e conversar, mas não conseguem entrar em contato e tocar na infraestrutura que deveriam estar gerenciando.</p><p>Para nosso último projeto de hackathon, decidimos eliminar essa limitação.</p><p>Nós construímos <strong>Infraestrutura ampliada</strong>: um copiloto de infraestrutura que não apenas dá conselhos, mas também cria, implanta, monitora e corrige seu ambiente em tempo real.</p><h2><strong>O problema: copiar, formatar, colar</strong></h2><p>Os agentes padrão operam em um vácuo. Se o seu app for desativado e custar US$ 5 milhões para a empresa, um agente padrão poderá ler para você o manual de instruções para corrigi-lo. Mas <em>você</em> ainda precisa fazer o trabalho. Você terá que copiar o código, reformatá-lo para o seu ambiente e colá-lo no terminal.</p><p>Queríamos um agente que entendesse a diferença entre <em>falar</em> sobre o Kubernetes e <em>configurar</em> o Kubernetes.</p><h2><strong>O motor: o que é o Elastic Agent Builder?</strong></h2><p>Para construir isso, não começamos do zero. Construímos sobre o <a href="https://www.elastic.co/elasticsearch/agent-builder"><strong>Elastic Agent Builder</strong></a>. Para quem não conhece, o Elastic Agent Builder é um framework projetado para desenvolver agentes rapidamente, e atua como a ponte entre um grande modelo de linguagem (LLM) (em nossa demonstração, usamos o Google Gemini) e dados privados armazenados no Elasticsearch.</p><p>O Agent Builder pode ser usado para IA conversacional, baseando-o em dados internos, como documentos ou registros. Mas seu recurso mais avançado é a capacidade de atribuir <strong>ferramentas</strong>. Essas ferramentas permitem que o LLM saia da interface de bate-papo para realizar tarefas específicas. Percebemos que, se levássemos esse recurso ao limite, poderíamos transformar o Agent Builder em uma potência de automação.</p><h2><strong>Fazendo funcionar: construindo a primeira versão</strong></h2><p>Quando começamos o projeto, sabíamos que queríamos que os agentes fossem capazes de mudar o mundo exterior. Tivemos uma ideia: e se construíssemos algum software &quot;runner&quot; (para executar qualquer comando que o agente pudesse imaginar no host)? E depois: e se os runners, o Elastic Agent Builder e o usuário estivessem em uma chamada tripla?</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8dc8317c1301b8eb7b89438529e8d8d17411c95a-1024x559.png" style="width: 100%;" /><p>Começamos desenvolvendo um projeto em Python, os Runners de Infraestrutura Ampliada, que era essencialmente um loop <code>while(true)</code> que consultava a API de conversas do Elastic Agent Builder a cada segundo e verificava uma sintaxe especial que havíamos criado:</p><pre><code>{
	"tool_name": "my_tool",
       "tool_arguments": "\{stringified json arguments\}"
}</code></pre><p>Depois, atualizamos o prompt para ensiná-lo nossa nova sintaxe de chamada de ferramenta. Bill é o mantenedor do <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, o framework mais popular para a criação de servidores MCP (Model Context Protocol) em Python. Ele começou a trabalhar usando o cliente FastMCP com este novo software de runner para montar servidores MCP e disponibilizar suas ferramentas ao runner. Quando o agente via isso, executava a chamada de ferramenta e <code>POST</code> os resultados de volta para a conversa como se o usuário tivesse enviado os resultados. Isso acionou o LLM para responder ao resultado, e seguimos em frente!</p><p>Isso foi ótimo, mas houve dois problemas principais:</p><ol><li>O agente despejaria todo esse JSON diretamente na conversa com o usuário.</li><li>O momento mais antigo em que mensagens eram visíveis pela API de conversas foi quando uma rodada de conversa foi concluída (ou seja, quando o LLM respondeu).</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7b3755aeae17722ff1bb9677712293e9195f96a0-1058x1034.png" style="width: 100%;" /><p>Então, começamos a descobrir como colocar isso em segundo plano.</p><p>Depois, passamos a dar ao agente uma ferramenta chamada <code>call_external_tool</code> com dois argumentos: o argumento <code>tool_name</code> e o argumento da ferramenta JSON stringified. Essa chamada de ferramenta externa não retornaria nada, mas, mais importante, seria visível na requisição <code>GET</code> para a API de conversas. Em seguida, demos permissão aos runners para escrever documentos diretamente no Elasticsearch, que o agente do Elastic Agent Builder poderia recuperar conforme necessário. O agente está sempre operando em resposta a uma mensagem do usuário, então precisamos iniciar o agente com uma mensagem de usuário para que ele busque os resultados e continue o processamento. Então pedimos aos agentes que inserissem uma pequena mensagem no chat para retomar a conversa:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/61ff59a57c68ed5fad492d19c0580644113a507d-1600x1321.png" style="width: 100%;" /><p>Então, agora tínhamos chamadas de ferramentas externas. No entanto, devido ao segundo problema mencionado acima, tivemos que eliminar a parte final do pontapé inicial. Caso contrário, cada chamada de ferramenta externa exigiria uma rodada completa de conversas para recuperar os resultados!</p><h2><strong>Aprimorando: introduzindo fluxos de trabalho</strong></h2><p>Além das chamadas da linguagem de consulta Elasticsearch (ES|QL) e da ferramenta de busca de índice, os agentes do Agent Builder podem chamar ferramentas baseadas em fluxo de trabalho da Elastic. Fluxos de trabalho da Elastic oferecem uma forma flexível e fácil de gerenciar para executar uma sequência arbitrária e uma lógica de ações. Para nossos propósitos, tudo o que precisamos é que o fluxo de trabalho armazene uma solicitação de ferramenta externa para o Elasticsearch e retorne uma ID para pesquisar os resultados. Isso resulta na seguinte definição simples de fluxo de trabalho:</p><pre><code>name: ai-tool-call
enabled: true
triggers:
  - type: manual
inputs:
  - name: runner_id
    type: string
  - name: tool_calls
    type: string

steps:
  - name: store_request
    type: elasticsearch.create
    with:
      index: distributed-tool-requests
      id: "{{inputs.runner_id}}_{{ execution.id }}"
      document:
        request_id: "{{ execution.id }}"
        runner_id: "{{inputs.runner_id}}"
        tool_call: "{{inputs.tool_calls}}"
        status: "unhandled"

  - name: output_result
    type: console
    with:
      message: "Called tool, with execution id: {{ execution.id }}. Use this ID to poll the results."</code></pre><p>Com isso, em vez de depender do pedido de chamada de ferramenta registrado na conversa, os runners podem simplesmente consultar o índice <code>distributed-tool-requests</code> do Elasticsearch para novas solicitações de ferramentas externas e registrar os resultados em outro índice do Elasticsearch com o <code>execution.id</code> fornecido.</p><p>Isso elimina os dois principais problemas mencionados acima:</p><ol><li>O histórico de conversas não está mais saturado com a carga útil das chamadas de ferramentas externas.</li><li>Como os runners estão consultando o índice do Elasticsearch em vez do histórico de conversas, eles não são bloqueados pela rodada de conversa a ser concluída para que os pedidos externos de ferramentas fiquem visíveis.</li></ol><p>O segundo ponto tem a grande vantagem de que o processamento das chamadas de ferramentas externas começa dentro da fase de pensamento do agente (e não quando a rodada de conversação é concluída). Isso nos permite instruir o LLM no prompt do sistema para sondar os resultados da ferramenta externa até que os resultados estejam disponíveis e elimina a necessidade da mensagem de inicialização. De modo geral, isso tem o efeito positivo de tornar a conversa mais natural: o LLM consegue processar várias solicitações de ferramentas externas em uma única rodada de conversa (em vez de exigir uma rodada de conversa para cada solicitação de ferramenta) e, portanto, consegue atender a solicitações de usuários mais complexas de uma só vez.</p><h2><strong>Juntando tudo</strong></h2><p>Para preencher a lacuna entre o LLM e o rack de servidores, desenvolvemos uma arquitetura específica usando as capacidades da ferramenta do Agent Builder:</p><ol><li><strong>Runners de Infraestrutura Ampliada:</strong> nós implantamos executantes leves dentro dos ambientes de destino (servidores, clusters Kubernetes, contas de nuvem). Esses executores são conectados diretamente à Elastic, usando endpoints seguros e segredos disponíveis apenas para cada um dos executores.</li><li><strong>Recuperação ES|QL:</strong> o copiloto usa o <strong>ES|QL da Elastic</strong> para realizar buscar híbridas. Não busca apenas conhecimento; busca <em>capacidades</em>. Ele consulta os executores runners para ver quais ferramentas estão disponíveis (por exemplo, <code>list_ec2_instances</code>, <code>install_helm_chart</code>).</li><li><strong>Execução do fluxo de trabalho:</strong> Quando o agente decide sobre um curso de ação, ele cria um fluxo de trabalho estruturado.</li><li><strong>Ciclo de feedback:</strong> os runners executam o comando localmente e enviam os resultados de volta ao Elasticsearch. O copiloto lê o resultado do índice e decide o próximo passo.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/76be256da722c1965971fc506502768bd890f0c4-1290x1076.png" style="width: 100%;" /><h2><strong>A demonstração: da interrupção à observabilidade</strong></h2><a href="https://videos.elastic.co/watch/bRgPJfJLeABjPMMhvy1sbB">Watch video on Vidyard</a><p>No vídeo, apresentamos dois cenários distintos que demonstram como essa arquitetura pode melhorar.</p><h3><strong>Cenário 1: Resgate DevOps</strong></h3><p>Começamos com um usuário entrando em pânico por causa de uma queda de 5 milhões de dólares causada por um ponto cego no cluster Kubernetes deles.</p><ul><li><strong>O pedido:</strong> &quot;como faço para garantir que isso não aconteça de novo?&quot;</li><li><strong>A ação:</strong> o agente não se limitou a oferecer um tutorial. Ele identificou o cluster, criou os espaços de nome necessários, gerou secrets do Kubernetes, instalou o OpenTelemetry Operator e forneceu instantaneamente um link para um dashboard de APM em tempo real.</li><li><strong>O resultado:</strong> total observabilidade do Kubernetes e insights do aplicativo sem que o usuário escreva uma única linha de YAML.</li></ul><h3><strong>Cenário 2: transferência de segurança</strong></h3><p>Uma regra fundamental da segurança da infraestrutura é que você não pode proteger o que não pode ver. Ao realizar nosso resgate de DevOps, o agente vê uma oportunidade de melhorar a segurança do ambiente.</p><p>Com um alerta iniciado a partir de uma investigação anterior relacionada ao Elastic Observability, demonstramos como um profissional de segurança pode interagir diretamente com sua infraestrutura: primeiro, para enumerar os ativos e recursos em seu ambiente de nuvem; e, segundo, para implantar as ferramentas necessárias para garantir que o ambiente esteja seguro.</p><ul><li><strong>Descoberta:</strong> o copiloto enumerou os recursos da AWS para o profissional de segurança e identificou uma lacuna crítica: uma instância do Amazon Elastic Compute Cloud (EC2) e um cluster do Amazon Elastic Kubernetes Service (EKS) com endpoints públicos sem proteção de endpoint.</li><li><strong>Remediação:</strong> com uma simples aprovação, o copiloto implantou <strong>Elastic Security</strong> <strong>detecção estendida e resposta (XDR) e detecção e resposta na nuvem (CDR)</strong> nos ativos vulneráveis, protegendo o ambiente em tempo real.</li><li><strong>Resultado:</strong> proteção dos ativos e recursos AWS implantados com segurança completa em tempo de execução.</li></ul><h2><strong>O futuro: tudo ampliado</strong></h2><p>Este projeto prova que o Elastic Agent Builder pode ser o cérebro central para operações distribuídas. Não estamos limitados apenas à infraestrutura. Nossa tecnologia de runners pode melhorar:</p><ul><li><strong>Synthetics ampliados:</strong> diagnosticando erros TLS em executores globais.</li><li><strong>Desenvolvimento ampliado:</strong> criando pull requests e implementando CAPTCHAs em serviços frontend.</li><li><strong>Operações aprimoradas:</strong> Reconfiguração automática de resolvedores de DNS durante uma interrupção.</li></ul><h2><strong>Experimente você mesmo</strong></h2><p>Acreditamos que o futuro da IA não se resume apenas ao suporte por chat; trata-se de <strong>Infraestrutura Ampliada</strong>. Trata-se de ter um parceiro que possa implantar, consertar, Observe e Protect ao seu lado.</p><p>Confira o código e experimente você mesmo com executores distribuídos (<a href="https://github.com/strawgate/augmented-infrastructure">GitHub</a>) e o Elastic Agent Builder no <a href="https://cloud.elastic.co/">Elastic Cloud Serverless</a> hoje mesmo!</p><ul><li>Crie um projeto serverless no Elastic Cloud.</li><li>Implante o código em um executor.</li><li>Prepare o runner.</li><li>Configure seu mcp.json.</li><li>Execute o runner, que criará seu agente e suas ferramentas automaticamente.</li><li>Converse com um agente que pode raciocinar, planejar e executar ações nos seus runners distribuídos!</li></ul><p><strong>A equipe: </strong><em>Alex, Bill, Gil, Graham e Norrie</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Estatísticas ES|QL mais rápidas com tabelas hash no estilo Swiss Tables]]></title>
            <description><![CDATA[Como o hashing inspirado em Swiss Tables e o design compatível com SIMD entregam acelerações consistentes e mensuráveis na linguagem de consulta do Elasticsearch (ES|QL).]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/esql-swiss-hash-stats</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/esql-swiss-hash-stats</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Hegarty, Matthew Alp, Nik Everet]]></dc:creator>
            <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/21036e031070f14faccb2b53b22723de2750c391-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Recentemente, substituímos partes importantes da implementação de tabelas hash do Elasticsearch por um design no estilo de Swiss Tables e observamos tempos de construção e iteração de 2 a 3 vezes mais rápidos em cargas de trabalho uniformes e de alta cardinalidade. O resultado é menor latência, melhor taxa de transferência e desempenho mais previsível para a linguagem de consulta do Elasticsearch (ES|QL) em estatísticas e operações de análise.</p><h2>Por que isso é importante</h2><p>A maioria dos fluxos de trabalho analíticos típicos acaba se resumindo ao agrupamento de dados. Seja calculando a média de bytes por host, contando eventos por usuário ou agregando métricas entre dimensões, a operação principal é a mesma: mapear chaves para grupos e atualizar agregados em execução.</p><p>Em pequena escala, praticamente qualquer tabela hash razoável funciona bem. Em grande escala (centenas de milhões de documentos e milhões de grupos distintos), os detalhes começam a importar. Fatores de carregamento, estratégia de sondagem, layout da memória e comportamento do cache podem fazer a diferença entre um desempenho linear e uma série de falhas de cache.</p><p>O Elasticsearch oferece suporte a essas cargas de trabalho há anos, mas estamos sempre buscando oportunidades para modernizar os algoritmos principais. Assim, avaliamos uma abordagem mais recente inspirada nas Swiss Tables e a aplicamos à maneira como o ES|QL calcula as estatísticas.</p><h2>Afinal, o que são Swiss Tables?</h2><p>As Swiss Tables são uma família de tabelas de hash modernas popularizadas pelo SwissTable do Google e posteriormente adotadas na Abseil e em outras bibliotecas.</p><p>Tabelas hash tradicionais gastam muito tempo atrás de ponteiros ou carregando chaves apenas para descobrir que não correspondem. O principal recurso das Swiss Tables é a capacidade de rejeitar a maioria das sondagens usando uma pequena estrutura de matriz residente em cache, armazenada separadamente das chaves e valores, chamada de <em>bytes de controle</em>, para reduzir drasticamente o tráfego de memória.</p><p>Cada byte de controle representa um único slot e, em nosso caso, codifica duas coisas: se o slot está vazio e uma pequena impressão digital derivada do hash. Esses bytes de controle são dispostos de maneira contígua na memória, geralmente em grupos de 16, tornando-os ideais para processamento de <a href="https://en.wikipedia.org/wiki/Single_instruction,_multiple_data">instrução única e dados múltiplos</a> (SIMD).</p><p>Em vez de sondar um slot de cada vez, as Swiss Tables analisam um bloco inteiro de controle de bytes usando instruções vetoriais. Em uma única operação, a CPU compara a impressão digital da chave de entrada com 16 slots e retira as entradas vazias. Somente os poucos candidatos que sobrevivem a esse processo rápido precisam ser carregados e comparados às chaves reais.</p><p>Esse design troca uma pequena quantidade extra de metadados por uma melhor localidade de cache e muito menos carregamentos aleatórios. À medida que a tabela cresce e as cadeias de sondagem se alongam, essas propriedades tornam-se cada vez mais valiosas.</p><h2>SIMD no centro</h2><p>A verdadeira estrela do espetáculo é o SIMD.</p><p>Bytes de controle não são apenas compactos, eles também são explicitamente projetados para serem processados com instruções vetoriais. Uma única comparação SIMD pode verificar 16 impressões digitais de uma vez, transformando o que normalmente seria um loop em algumas operações amplas. Por exemplo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bd418778f0c6144f8f5f18419f6220ac0c935c7a-903x407.png" style="width: 100%;" /><p>Na prática, isso significa:</p><ul><li>Menos ramificações.</li><li>Cadeias de sondagem mais curtas.</li><li>Menos carregamentos da memória de chave e valor.</li><li>Utilização muito melhor das unidades de execução da CPU.</li></ul><p>A maioria das pesquisas nunca passa da verificação do byte de controle. Quando isso acontece, o restante do trabalho é focado e previsível. Esse é exatamente o tipo de carga de trabalho que CPUs modernas fazem bem.</p><h2>SIMD nos bastidores</h2><p>Para os leitores que gostam de espiar os bastidores, aqui está o que acontece ao inserir uma nova chave na tabela. Usamos a Panama Vector API com vetores de 128 bits, operando assim em 16 bytes de controle em paralelo.</p><p>O trecho a seguir mostra o código gerado em um Intel Rocket Lake com AVX-512. Embora as instruções reflitam esse ambiente, o design não depende do AVX-512. As mesmas operações vetoriais de alto nível são emitidas em outras plataformas usando instruções equivalentes (por exemplo, AVX2, SSE ou NEON).</p><pre><code>; Load 16 control bytes from the control block
vmovdqu xmm0, XMMWORD PTR [r9+r10*1+0x10]

; Broadcast the 7-bit fingerprint of the new key across the vector
vpbroadcastb xmm1, r11d

; Compare all 16 control bytes to the new fingerprint
vpcmpeqb k7, xmm0, xmm1
kmovq rbx, k7

; Check if any matches were found
test rbx, rbx
jne <handle_match></code></pre><p>Cada instrução tem um papel claro no processo de inserção:</p><ul><li><code>vmovdqu</code>: Carrega 16 bytes de controle consecutivos no registrador <code>xmm0</code> de 128 bits.</li><li><code>vpbroadcastb</code>: Replica a impressão digital de 7 bits da nova chave em todas as faixas do registrador <code>xmm1</code>.</li><li><code>vpcmpeqb</code>: Compara cada byte de controle com a impressão digital transmitida, produzindo uma máscara de possíveis correspondências.</li><li><code>kmovq</code> + <code>test</code>: Move a máscara para um registrador de uso geral e verifica rapidamente se existe uma correspondência.</li></ul><p>Finalmente, decidimos sondar grupos de 16 bytes de controle por vez, pois o benchmarking mostrou que a expansão para 32 ou 64 bytes com registros mais amplos não oferecia nenhum benefício mensurável de desempenho.</p><h2>Integração em ES|QL</h2><p>Adotar o hashing com a técnica de Swiss Tables no Elasticsearch não foi uma simples substituição. O ES|QL tem requisitos rigorosos em relação à contabilidade de memória, segurança e integração com o restante do motor de computação.</p><p>Integramos a nova tabela hash de maneira precisa com o gerenciamento de memória do Elasticsearch, incluindo o reciclador de páginas e a contabilização de mecanismos de interrupção, garantindo que as alocações permaneçam visíveis e limitadas. As agregações do Elasticsearch são armazenadas densamente e indexadas por uma ID de grupo, mantendo o layout da memória compacto e rápido para iterações, além de possibilitar certas otimizações de desempenho ao permitir acesso aleatório.</p><p>Para chaves de bytes de comprimento variável, armazenamos em cache o hash completo junto com a ID do grupo. Isso evita o recálculo de códigos hash caros durante a sondagem e melhora a localidade do cache ao manter os metadados relacionados próximos uns dos outros. Durante o rehashing, podemos confiar no hash e bytes de controle em cache sem inspecionar os próprios valores, mantendo os custos de redimensionamento baixos.</p><p>Uma simplificação importante em nossa implementação é que as entradas nunca são excluídas. Isso elimina a necessidade de <em>lápides</em> (marcadores para identificar slots ocupados anteriormente) e permite que os slots vazios permaneçam realmente vazios, o que melhora ainda mais o comportamento da sondagem e mantém a eficiência das varreduras dos bytes de controle.</p><p>O resultado é um design que se encaixa naturalmente no modelo de execução do Elasticsearch, preservando as características de desempenho que tornam as Swiss Tables interessantes.</p><h2>Qual é o desempenho?</h2><p>Em pequenas cardinalidades, as Swiss Tables apresentam desempenho aproximadamente equivalente à implementação existente. Isso é esperado: quando as tabelas são pequenas, os efeitos de cache dominam menos e há pouca sondagem para otimizar.</p><p>À medida que a cardinalidade aumenta, o cenário logo muda.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24900afc47ab07b0e9933f6117b99d0f4613f794-962x599.png" style="width: 100%;" /><p>O heatmap acima mostra fatores de melhoria de tempo para diferentes tamanhos de chave (8, 32, 64 e 128 bytes) entre cardinalidades de 1.000 a 10.000.000 de grupos. À medida que a cardinalidade aumenta, o fator de melhoria aumenta constantemente, chegando a 2 a 3 vezes para distribuições uniformes.</p><p>Essa tendência é exatamente o que o design prevê. Uma maior cardinalidade leva a cadeias de sondagem mais longas em tabelas hash tradicionais, enquanto a sondagem com a técnica Swiss Tables continua resolvendo a maioria das pesquisas em blocos de bytes de controle compatíveis com SIMD.</p><h2>O comportamento do cache conta a história</h2><p>Para entender melhor as acelerações, executamos os mesmos <a href="https://github.com/elastic/elasticsearch/pull/139343/files#diff-d0e0cc91a7495bf36b2d44eacce95f5185d01879e5f6c38089ac7a89aad17da7"><code>benchmarks</code></a> JMH sob o Linux <code>perf</code> e capturamos cache e estatísticas do TLB.</p><p>Em comparação com a implementação original, a versão Swiss Tables realiza cerca de 60% menos referências de cache no geral. Os carregamentos de cache no último nível caem mais de 4 vezes, e as falhas de carregamento da LLC caem mais de 6 vezes. Como as falhas de LLC costumam se traduzir diretamente em acessos à memória principal, essa redução sozinha explica grande parte da melhoria de ponta a ponta.</p><p>Mais próximo da CPU, observamos menos falhas de cache de dados L1 e quase 6 vezes menos falhas de dados TLB, indicando uma localidade espacial mais precisa e padrões de acesso à memória mais previsíveis.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6e49b7609fba83e33692cb9834552b6ca7e42a83-998x499.png" style="width: 100%;" /><p>Esse é o benefício prático dos bytes de controle compatíveis com SIMD. Em vez de carregar repetidamente chaves e valores de locais de memória dispersos, a maioria das sondagens é resolvida analisando uma estrutura compacta residente no cache. Menos memória acessada significa menos falhas, e menos falhas significam consultas mais rápidas.</p><h2>Conclusão</h2><p>Ao adotar um design de tabela hash com a técnica Swiss Tables e apostar fortemente na sondagem compatível com SIMD, alcançamos uma aceleração de 2 a 3 vezes para cargas de trabalho de estatísticas ES|QL de alta cardinalidade, além de um desempenho mais estável e previsível.</p><p>Este trabalho destaca como estruturas de dados modernas conscientes da CPU podem desbloquear ganhos substanciais, mesmo para problemas já conhecidos, como tabelas hash. Há mais espaço para explorar aqui, como especializações adicionais de tipos primitivos e uso em outros caminhos de alta cardinalidade, como joins, todos eles apenas parte do esforço mais amplo e contínuo para modernizar continuamente os internos do Elasticsearch.</p><p>Se você tiver interesse nos detalhes ou quiser acompanhar o trabalho, confira o rastreamento de progresso de <a href="https://github.com/elastic/elasticsearch/pull/139343">pull request</a> e <a href="https://github.com/elastic/elasticsearch/issues/138799">meta issue</a> no Github.</p><p>Boa sorte com o hashing!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gerenciando a memória agentiva com o Elasticsearch]]></title>
            <description><![CDATA[Criando agentes mais conscientes do contexto e eficientes gerenciando memórias usando o Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agentic-memory-management-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agentic-memory-management-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Someshwaran Mohankumar]]></dc:creator>
            <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/80907fd072e72d6ec902470b449c9f337957a0d7-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Na disciplina emergente da <strong>engenharia de contexto</strong>, é fundamental fornecer aos agentes de IA as informações certas no momento certo. Um dos aspectos mais importantes da engenharia de contexto é o gerenciamento da <strong>memória</strong> de uma IA. Assim como os humanos, sistemas de IA dependem tanto de memória de curto prazo quanto de memória de longo prazo para recordar informações. Se quisermos que agentes de grandes modelos de linguagem (LLM) mantenham conversas lógicas, lembrem das preferências do usuário ou construam sobre resultados ou respostas anteriores, precisamos equipá-los com mecanismos de memória eficazes.</p><p>Afinal, tudo no contexto influencia as respostas da IA. O princípio <em>Lixo entra, lixo saí</em> continua válido.</p><p>Neste artigo, vamos apresentar o que as memórias de curto e longo prazo significam para agentes de IA, especificamente:</p><ul><li>A diferença entre memória de curto e longo prazo.</li><li>Como elas se relacionam com as técnicas de Retrieval-Augmented Generation (RAG) com bancos de dados vetoriais, como o Elasticsearch, e por que é necessário um gerenciamento cuidadoso da memória.</li><li>Os riscos de negligenciar a memória, incluindo transbordamento de contexto e envenenamento do contexto.</li><li>Boas práticas, como a eliminação do contexto, o resumo e a recuperação apenas do que é relevante, visam manter a memória de um agente útil e segura.</li><li>Por fim, vamos abordar como a memória pode ser compartilhada e propagada em sistemas multiagente para permitir que agentes colaborem sem confusão usando o Elasticsearch.</li></ul><h2>Memória de curto prazo versus memória de longo prazo em agentes de IA</h2><p><strong><em>Memória de curto prazo</em></strong> em um agente de IA geralmente se refere ao contexto conversacional imediato ou estado — essencialmente, o histórico atual da conversa ou mensagens recentes na sessão ativa. Isso inclui a consulta mais recente do usuário e as trocas de mensagens recentes. É muito semelhante à informação que uma pessoa mantém em mente durante uma conversa em andamento.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4fbcc6f68055b2bccefc4176297a4ca50056dc0d-764x498.png" style="width: 100%;" /><p>Frameworks de IA frequentemente mantêm essa memória transitória como parte do estado do agente (por exemplo, usando um mecanismo de ponto de verificação para armazenar o estado da conversa, conforme abordado <a href="https://docs.langchain.com/oss/python/langgraph/persistence#checkpoints">por este exemplo do LangGraph</a>). A memória de curto prazo tem <strong><em>escopo de sessão</em></strong>, ou seja, existe em uma única conversa ou tarefa e é redefinida ou apagada quando a sessão termina, a menos que seja explicitamente salva em outro lugar. Um exemplo de memória de curto prazo ligada a sessões seria o <strong><a href="https://help.openai.com/en/articles/8914046-temporary-chat-faq">chat temporário</a> </strong>disponível no ChatGPT.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/150bdf209cda5ed20b59cddf34e624ad1a8016aa-1100x577.png" style="width: 100%;" /><p><strong><em>A memória de longo prazo</em></strong>, por outro lado, refere-se a informações que persistem <strong>ao longo de conversas ou sessões</strong>. Este é o conhecimento que um agente retém ao longo do tempo, fatos que aprendeu anteriormente, preferências do usuário ou quaisquer dados que pedimos para ele lembrar permanentemente.</p><p>A memória de longo prazo geralmente é implementada armazenando e recuperando dados de uma fonte externa, como um arquivo ou banco de dados vetorial fora do contexto imediato da janela. Diferentemente do histórico de chats de curto prazo, a memória de longo prazo não é incluída automaticamente em todas as solicitações. Em vez disso, com base em um determinado cenário, o agente deve <strong>recordá-lo</strong> ou recuperá-lo quando as ferramentas relevantes forem invocadas. Na prática, a memória de longo prazo pode incluir informações de perfil do usuário, respostas ou análises anteriores produzidas pelo agente, ou uma base de conhecimento que o agente pode consultar.</p><p>Por exemplo, se você tiver um agente de planejamento de viagens, a <em>memória de curto prazo</em> conterá detalhes da consulta de viagem atual (datas, destino, orçamento) e quaisquer perguntas subsequentes feitas durante o chat; enquanto a <em>memória de longo prazo</em> poderá armazenar as preferências gerais de viagem do usuário, itinerários anteriores e outros dados compartilhados em sessões anteriores. Quando o usuário retornar posteriormente, o agente poderá recorrer a esse histórico (por exemplo, o usuário adora praias e montanhas, tem um orçamento médio de 100.000 rúpias indianas, possui uma lista de lugares para visitar antes de morrer e prefere vivenciar história e cultura em vez de atrações voltadas para crianças), de modo que não trate o usuário como uma folha em branco a cada vez.</p><p>A memória de curto prazo (histórico de chats) fornece contexto e continuidade imediatos, enquanto a memória de longo prazo fornece um contexto mais amplo que o agente pode usar quando necessário. Os frameworks de agentes de IA mais avançados permitem ambas as coisas: mantêm o controle dos diálogos recentes para manter o contexto <em>e</em> oferecem mecanismos para consultar ou armazenar informações em um repositório de longo prazo. O gerenciamento da memória de curto prazo garante que ela permaneça dentro da janela de contexto, enquanto o gerenciamento da memória de longo prazo ajuda o agente a basear as respostas com base em interações e personalidades anteriores.</p><h2>Memória e RAG na engenharia de contexto</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46635aa11ceff89b8d6a26ac3e22da52407d82f3-1600x900.png" style="width: 100%;" /><p><strong><em>Como damos a um agente de IA uma memória útil de longo prazo na prática?</em></strong></p><p>Uma abordagem proeminente para a memória de longo prazo é a <strong><em>memória semântica</em></strong>, frequentemente implementada por meio de <strong>retrieval-augmented generation (RAG)</strong>. Isso envolve acoplar o LLM a um armazenamento de conhecimento externo ou a um datastore habilitado por vetor, como o Elasticsearch. Quando o LLM precisa de informações além do que está no prompt ou em seu treinamento integrado, ele realiza uma recuperação semântica contra o Elasticsearch e injeta os resultados mais relevantes no prompt como contexto. Dessa forma, o contexto efetivo do modelo inclui não apenas a conversa recente (memória de curto prazo), mas também fatos pertinentes de longo prazo obtidos em tempo real. O LLM então fundamenta sua resposta tanto em seu próprio raciocínio quanto nas informações recuperadas, combinando efetivamente memória de curto prazo e memória de longo prazo para produzir uma resposta mais precisa e consciente do contexto.</p><p>O <strong>Elasticsearch </strong>pode ser usado para implementar memória de longo prazo para agentes de IA. Aqui está um exemplo de alto nível de como o contexto pode ser recuperado do Elasticsearch para memória de longo prazo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/41ccbc7b5171e8170ac300139a963c0708816ba6-1600x900.png" style="width: 100%;" /><p>Dessa forma, o agente &quot;se lembra&quot; pesquisando dados relevantes em vez de armazenar tudo em seu prompt limitado, <strong>onde isso leva a diferentes riscos.</strong></p><p><strong>Usar RAG com o Elasticsearch ou qualquer armazenamento vetorial oferece múltiplos benefícios:</strong></p><p>Primeiro, ele <strong>amplia o conhecimento</strong> do modelo além do limite de treinamento. O agente pode recuperar informações atualizadas ou dados específicos do domínio que o LLM talvez não conheça. Isso é crucial para perguntas sobre eventos recentes ou tópicos especializados.</p><p>Em segundo lugar, recuperar o contexto sob demanda ajuda a reduzir alucinações, especialmente porque os LLMs não são treinados com dados proprietários ou altamente especializados relativos ao seu caso de uso específico, o que é muito provável que os exponha a alucinações. Em vez de o LLM adivinhar ou inventar novas informações, como tem sido incentivado pela avaliação, conforme destacado em um artigo recente da OpenAI (<a href="https://arxiv.org/pdf/2509.04664">Why Language Models Hallucinate</a>), o modelo pode ser fundamentado em referências factuais do Elasticsearch. Naturalmente, o LLM depende da confiabilidade dos dados no armazenamento vetorial para realmente evitar desinformação e os dados relevantes são recuperados de acordo com as medidas de relevância do núcleo.</p><p>Terceiro, o RAG permite que um agente trabalhe com bases de conhecimento muito maiores do que qualquer coisa que você poderia encaixar em um prompt. Em vez de inserir documentos inteiros, como longos artigos de pesquisa ou documentos de políticas, na janela de contexto e correr o risco de sobrecarga ou de informações irrelevantes <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-poisoning">contaminarem</a> o raciocínio do modelo, o RAG se baseia em <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">fragmentação</a>. Documentos grandes são divididos em partes menores e semanticamente significativas, e o sistema recupera apenas os poucos trechos mais relevantes para a consulta. Dessa forma, o modelo não precisa de um contexto de um milhão de tokens para parecer conhecedor; ele só precisa de acesso aos pedaços certos de um corpus muito maior.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e6897356c9f0940e35a63d005e9cd20bc33e5dd7-1600x931.png" style="width: 100%;" /><p>Vale ressaltar que, com o aumento das janelas de contexto do LLM (<a href="https://www.anthropic.com/news/1m-context">alguns modelos agora suportam centenas de milhares ou até milhões de tokens</a><em>)</em>, surgiu um debate sobre se o RAG está &quot;morto&quot;. Por que não enviar todos os dados para o prompt? Se você pensa da mesma forma, consulte este maravilhoso artigo de meus colegas Jeffrey Rengifo e Eduard Martin, <a href="https://www.elastic.co/search-labs/blog/rag-vs-long-context-model-llm">Longer context ≠ better: Why RAG still matters</a>. Isso evita o problema de &quot;lixo entra, lixo sai&quot;: o LLM fica focado nos poucos pedaços que importam, em vez de passar por meio de ruído.</p><p>Dito isso, integrar o Elasticsearch ou qualquer armazenamento vetorial em uma arquitetura de agente de IA fornece <strong>memória de longo prazo</strong>. O agente armazena conhecimento externamente e o recolhe como contexto de memória quando necessário. Isso poderia ser implementado como uma <em>arquitetura</em>, onde, após cada consulta do usuário, o agente realiza uma busca no Elasticsearch por informações relevantes e então adiciona os principais resultados ao prompt antes de chamar o LLM. A resposta também pode ser salva de volta no armazenamento de longo prazo se contiver informações novas úteis (criando um ciclo de retroalimentação de aprendizado). Ao usar essa memória baseada em recuperação, o agente permanece informado e atualizado, sem precisar condensar tudo o que sabe em cada prompt, mesmo que a janela de contexto suporte <em>um milhão de tokens</em>. Essa técnica é uma pedra angular da engenharia de contexto, combinando os pontos fortes da recuperação de informação e da IA generativa. </p><p>Aqui está um exemplo de um estado gerenciado de conversa em memória usando o sistema de ponto de verificação do LangGraph para memória de curto prazo durante a sessão. (Consulte nosso <a href="https://github.com/someshwaranM/elastic-context-engineering-short-term-long-term-memory">app de engenharia de contexto de suporte</a>).</p><pre><code># Initialize chat memory (Note: This is in-memory only, not persistent)
memory = MemorySaver()

# Create a LangGraph agent
langgraph_agent = create_react_agent(model=llm, tools=tools, checkpointer=memory)

...
...
# Only process and display checkpoints if verbose mode is enabled
if args.verbose:
    # List all checkpoints that match a given configuration
    checkpoints = memory.list({"configurable": {"thread_id": "1"}})
    # Process the checkpoints
    process_checkpoints(checkpoints)</code></pre><p>Veja como ele armazena <strong>pontos de verificação</strong>:</p><pre><code>Checkpoint:
Timestamp: 2025-12-30T09:19:41.691087+00:00
Checkpoint ID: 1f0e560a-c2fa-69ec-8001-14ee5373f9cf
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)
Agent:  (Message ID: lc_run--019b6e8e-553f-7b52-8796-a8b1fbb206a4-0)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.350507+00:00
Checkpoint ID: 1f0e560a-b631-6a08-8000-7796d108109a
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.349027+00:00
Checkpoint ID: 1f0e560a-b62e-6010-bfff-cbebe1d865f6</code></pre><p>Para a memória de longo prazo, veja como realizamos a busca semântica no Elasticsearch para recuperar conversas anteriores relevantes usando embeddings de vetor após resumir e a indexar pontos de verificação no Elasticsearch.</p><pre><code>Functions: 
retrieve_from_elasticsearch() 

# Enhanced Elasticsearch retrieval with rank_window and verbose display
def retrieve_from_elasticsearch(query: str, k: int = 5, rank_window: int = None) -> tuple[List[Dict[str, Any]], str]:
    """
    Retrieve context from Elasticsearch with score-based ranking
    
    Args:
        query: Search query
        k: Number of results to return
        rank_window: Number of candidates to retrieve before ranking (default: args.rank_window)
        
    Returns:
        Tuple of (retrieved_documents, formatted_context_string)
    """
    if not es_client or not es_index_name:
        return [], "Elasticsearch is not available. Cannot search long-term memory."
    
    if rank_window is None:
        rank_window = args.rank_window
    
    try:
        # Check if index exists and has documents
        if not es_client.indices.exists(index=es_index_name):
            return [], "No previous conversations stored in long-term memory yet."
        
        # Get document count
        try:
            doc_count = es_client.count(index=es_index_name)["count"]
            if doc_count == 0:
                return [], "Long-term memory is empty. No previous conversations to search."
        except Exception as e:
            return [], f"Error checking memory: {str(e)}"
        
        # Generate embedding for the query
        try:
            query_embedding = embeddings.embed_query(query)
        except Exception as e:
            return [], f"Error generating embedding: {str(e)}"
        
        # Perform semantic search using kNN with rank_window
        try:
            search_body = {
                "knn": {
                    "field": "vector",
                    "query_vector": query_embedding,
                    "k": k,
                    "num_candidates": rank_window  # Retrieve more candidates, then rank top k
                },
                "_source": ["text", "content", "message_type", "timestamp", "thread_id"],
                "size": k
            }
            
            response = es_client.search(index=es_index_name, body=search_body)
            
            if not response.get("hits") or len(response["hits"]["hits"]) == 0:
                return [], "No relevant previous conversations found in long-term memory."
            
            # Extract documents with scores
            retrieved_docs = []
            for hit in response["hits"]["hits"]:
                source = hit["_source"]
                score = hit["_score"]
                retrieved_docs.append({
                    "content": source.get("content", source.get("text", "")),
                    "message_type": source.get("message_type", "unknown"),
                    "timestamp": source.get("timestamp", "unknown"),
                    "thread_id": source.get("thread_id", "unknown"),
                    "score": score
                })
            
            # Format context string
            context_parts = []
            for i, doc in enumerate(retrieved_docs, 1):
                context_parts.append(doc["content"])
            
            context_string = "\n\n".join(context_parts)
            
            # Verbose display
            if args.verbose:
                rich.print(f"\n[bold yellow]🔍 RETRIEVAL ANALYSIS[/bold yellow]")
                rich.print("="*80)
                rich.print(f"[blue]Query:[/blue] {query}")
                rich.print(f"[blue]Retrieved:[/blue] {len(retrieved_docs)} documents (from {rank_window} candidates)")
                rich.print(f"[blue]Total context length:[/blue] {len(context_string)} characters\n")
                
                for i, doc in enumerate(retrieved_docs, 1):
                    rich.print(f"[cyan]📄 Document {i} | Score: {doc['score']:.4f} | Type: {doc['message_type']}[/cyan]")
                    rich.print(f"[cyan]   Timestamp: {doc['timestamp']} | Thread: {doc['thread_id']}[/cyan]")
                    content_preview = doc['content'][:200] + "..." if len(doc['content']) > 200 else doc['content']
                    rich.print(f"[cyan]   Content: {content_preview}[/cyan]")
                    rich.print("-" * 80)
            
            return retrieved_docs, context_string
            
        except Exception as e:
            return [], f"Error searching memory: {str(e)}"
            
    except Exception as e:
        return [], f"Error accessing long-term memory: {str(e)}"</code></pre><p>Agora que exploramos como a memória de curto prazo e a memória de longo prazo são indexadas e buscadas usando os pontos de verificação do LangGraph no Elasticsearch, vamos tirar um tempo para entender por que a indexação e o despejo das conversas completas podem ser arriscados.</p><h2>Riscos de não gerenciar a memória de contexto</h2><p>Como estamos falando muito sobre engenharia de contexto, junto com memória de curto e longo prazo, vamos entender o que acontece se não gerenciarmos bem a memória e o contexto de um agente.</p><p>Infelizmente, muitas coisas podem dar errado quando o contexto de uma IA se torna extremamente longo ou contém informações erradas. À medida que as janelas de contexto aumentam, <strong>surgem novos modos de falha</strong>, como:</p><ul><li><strong>Envenenamento de contexto</strong></li><li><strong>Distração contextual</strong></li><li><strong>Confusão de contexto</strong></li><li><strong>Conflito de contexto</strong></li><li><strong>Vazamento de contexto e conflitos de conhecimento</strong></li><li><strong>Alucinações e desinformação</strong></li></ul><p>Vamos analisar esses problemas e outros riscos que surgem do gerenciamento inadequado de contexto:</p><h3>Envenenamento de contexto</h3><p>O <em>envenenamento do contexto</em> refere-se a quando informações incorretas ou prejudiciais acabam no contexto e &quot;envenenam&quot; as saídas subsequentes do modelo. Um exemplo comum é uma alucinação do modelo que é tratada como fato e inserida no histórico da conversa. O modelo pode então construir sobre esse erro em respostas posteriores, agravando o erro. Em ciclos iterativos de agentes, uma vez que uma informação falsa entra no contexto compartilhado (por exemplo, em um resumo das notas de trabalho do agente), ela pode ser reforçada repetidamente. </p><p><a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">Pesquisadores da DeepMind, no lançamento do relatório Gemini 2.5</a> (TL;DR, veja <a href="https://www.dbreunig.com/2025/06/17/an-agentic-case-study-playing-pok%C3%A9mon-with-gemini.html">aqui</a>), observaram o seguinte em um agente que jogava <em>Pokémon</em>: se o agente alucinasse um estado incorreto de jogo e isso fosse registrado em seu <em>contexto </em>(a memória de objetivos), o agente criaria <strong>estratégias sem sentido</strong> em torno de um objetivo impossível e ficaria preso. Em outras palavras, uma memória envenenada pode levar o agente pelo caminho errado indefinidamente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3f2facf5aad67613ad557422e09ec23a66adc0ed-1600x1388.png" style="width: 100%;" /><p>O envenenamento de contexto pode acontecer inocentemente (por engano) ou até mesmo de forma mal-intencionada, por exemplo, por meio de ataques de injeção de prompt, onde um usuário ou terceiro introduz uma instrução oculta ou um fato falso que o agente então lembra e segue.</p><p><strong>Contramedidas recomendadas:</strong></p><p>Com base nos insights de <a href="https://www.wiz.io/academy/data-poisoning">Wiz</a>, <a href="https://zerlo.net/en/blog/what-is-llm-data-poisoning">Zerlo</a> e <a href="https://www.anthropic.com/research/small-samples-poison">Anthropic</a>, as contramedidas para envenenamento de contexto se concentram em evitar que informações ruins ou enganosas entrem no prompt, na janela de contexto ou no pipeline de recuperação de um LLM. Os principais passos incluem:</p><ul><li>Verifique o contexto constantemente: monitore a conversa ou o texto recuperado para qualquer coisa suspeita ou prejudicial, não apenas o prompt inicial.</li><li>Use fontes confiáveis: pontue ou rotule documentos com base na credibilidade para que o sistema prefira informações confiáveis e ignore dados com pontuação baixa.</li><li>Identifique dados incomuns: use ferramentas que detectem conteúdo estranho, fora de lugar ou manipulado, e remova-o antes que o modelo os utilize.</li><li>Filtre entradas e saídas: adicione proteções de segurança para que textos prejudiciais ou enganosos não possam entrar facilmente no sistema ou ser repetidos pelo modelo.</li><li>Mantenha o modelo atualizado com dados limpos: atualize regularmente o sistema com informações verificadas para combater qualquer dado errado que tenha escapado.</li><li>Com supervisão humana: peça para as pessoas revisarem as saídas importantes ou compará-las com fontes conhecidas e confiáveis.</li></ul><p>Hábitos simples do usuário também ajudam, como redefinir chats longos, compartilhar apenas informações relevantes, dividir tarefas complexas em etapas menores e manter anotações claras fora do modelo.</p><p>Juntas, essas medidas criam uma defesa em camadas que protege os LLMs contra envenenamento do contexto e mantém as saídas precisas e confiáveis.</p><p>Sem contramedidas mencionadas aqui, um agente pode lembrar-se de instruções, como ignorar diretrizes anteriores<em> </em>ou fatos triviais inseridos por um invasor, levando a saídas prejudiciais.</p><h3>Distração contextual</h3><p>A <em>distração de contexto</em> ocorre quando um contexto se alonga tanto que o modelo foca demais no contexto, negligenciando o que aprendeu durante o treinamento. Em casos extremos, isso se assemelha <a href="https://en.wikipedia.org/wiki/Catastrophic_interference"><em>ao esquecimento catastrófico</em></a>; ou seja, o modelo efetivamente &quot;esquece&quot; seu conhecimento subjacente e fica excessivamente apegado à informação que lhe é apresentada. Estudos anteriores mostraram que LLMs frequentemente perdem o foco quando o prompt é extremamente longo.</p><p>O agente Gemini 2.5, por exemplo, suportava uma janela de um milhão de tokens, mas quando seu contexto crescia além de um certo ponto (na ordem de 100.000 tokens em um experimento), ele começava a <strong>se fixar em repetir suas ações passadas</strong> em vez de apresentar novas soluções. De certa forma, o agente tornou-se prisioneiro de sua extensa história. Ele continuava observando seu longo log de movimentos anteriores (o contexto) e imitando-os, em vez de usar seu conhecimento de treinamento subjacente para criar estratégias novas e inovadoras.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e08e5b6d2e8ec7e3511d455985eed3d7fa6241e0-1352x636.png" style="width: 100%;" /><p>Isso é contraproducente. Queremos que o modelo use o contexto relevante para ajudar no raciocínio, não para sobrepor sua capacidade de pensar. Notavelmente, mesmo modelos com janelas enormes exibem essa <a href="https://research.trychroma.com/context-rot"><em>deterioração de contexto</em></a>: seu desempenho se degrada de forma não uniforme à medida que mais tokens são adicionados. Parece haver um <em>orçamento de atenção</em>. Como humanos com memória de trabalho limitada, um LLM tem uma capacidade finita para atender a tokens, e à medida que esse orçamento é esticado, sua precisão e foco caem.</p><p>Como mitigação, você pode evitar distração do contexto usando fragmentação, engenharia das informações corretas, resumo regular do contexto e técnicas de avaliação e monitoramento para medir a precisão da resposta usando pontuação.</p><p>Esses métodos mantêm o modelo baseado no contexto relevante e em seu treinamento subjacente, reduzindo o risco de distração e melhorando a qualidade geral do raciocínio.</p><h3>Confusão de contexto</h3><p>A <em>confusão de contexto</em> ocorre quando o conteúdo supérfluo no contexto é usado pelo modelo para gerar uma resposta de baixa qualidade. Um ótimo exemplo é fornecer a um agente um grande conjunto de ferramentas ou definições de API que ele pode usar. Se muitas dessas ferramentas não estiverem relacionadas à tarefa atual, o modelo ainda pode tentar usá-las de forma inadequada, simplesmente porque elas estão presentes no contexto. Experimentos descobriram que fornecer <em>mais</em> ferramentas ou documentos pode <em>prejudicar</em> o desempenho se não forem todos necessários. O agente começa a cometer erros, como chamar a função errada ou fazer referência a um texto irrelevante. </p><p>Em um caso, um pequeno modelo <strong>Llama 3.1 8B</strong> falhou em uma tarefa ao receber 46 ferramentas para considerar, mas teve sucesso quando recebeu apenas 19 ferramentas. As ferramentas extras criaram confusão, mesmo que o contexto estivesse dentro dos limites de duração. A questão subjacente é que qualquer informação no prompt será <em>atendida</em> pelo modelo. Se não souber ignorar algo, esse algo pode influenciar sua saída de maneiras indesejadas. Pedaços irrelevantes podem “roubar” parte da atenção do modelo e desviá-lo (por exemplo, um documento irrelevante pode fazer com que o agente responda a uma pergunta diferente da feita). A confusão de contexto frequentemente se manifesta como o modelo produzindo uma resposta de baixa qualidade que integra contextos não relacionados. Consulte o trabalho de pesquisa: <a href="https://arxiv.org/pdf/2411.15399">Menos é mais: otimizando a chamada de funções para execução LLM em dispositivos de borda.</a></p><p>Isso nos lembra que mais contexto nem sempre é melhor, especialmente se não for <strong>selecionado</strong> para ser relevante.</p><h3>Conflito de contexto</h3><p>O <em>conflito de contexto</em> ocorre quando <strong>partes do contexto se contradizem</strong>, causando inconsistências internas que comprometem o raciocínio do modelo. Um conflito pode acontecer se o agente acumular várias informações que estão em conflito. </p><p>Por exemplo, imagine um agente que obteve dados de duas fontes: uma diz que <em>o voo A parte às 17h</em>, e a outra diz que <em>o voo A parte às 18h</em>. Se ambos os fatos estiverem presentes no contexto, o modelo inadequado não terá como saber qual está correto; poderá ficar confuso ou produzir uma resposta incorreta ou não similar.</p><p>O conflito de contexto também ocorre com frequência em conversas com várias interações, em que as <strong>tentativas anteriores</strong> de resposta do modelo ainda permanecem no contexto junto com informações refinadas posteriormente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/500d7a80dc8db1923f9b5ca84728eed64fa296f7-1316x580.png" style="width: 100%;" /><p>Um <a href="https://arxiv.org/pdf/2505.06120">estudo realizado</a> pela Microsoft e pela Salesforce mostra que, ao dividir uma consulta complexa em várias interações com o chatbot (adicionando detalhes gradualmente), a precisão final cai significativamente em comparação com a apresentação de todos os detalhes em uma única solicitação. Por quê? Porque as primeiras interações contêm respostas intermediárias parciais ou incorretas do modelo, e essas permanecem no contexto. Quando o modelo tenta responder posteriormente com todas as informações, sua <em>memória</em> ainda inclui as tentativas erradas, que entram em conflito com as informações corrigidas e o desviam do caminho. Basicamente, o contexto da conversa entra em conflito consigo mesmo. O modelo pode, sem querer, usar um contexto desatualizado (de uma interação anterior) que não se aplica depois que novas informações são adicionadas.</p><p>Em sistemas de agentes, o choque de contexto é especialmente perigoso, pois um agente pode combinar saídas de diferentes ferramentas ou subagentes. Se essas saídas discordarem, o contexto agregado será inconsistente. O agente pode, então, ficar travado ou produzir resultados sem sentido ao tentar conciliar as contradições. Evitar conflitos de contexto envolve garantir que o contexto seja <strong>novo e consistente</strong>,<strong> </strong>por exemplo, limpar ou atualizar qualquer informação desatualizada e não misturar fontes que não tenham sido verificadas quanto à consistência.</p><h3>Vazamento de contexto e conflitos de conhecimento</h3><p>Em sistemas onde múltiplos agentes ou usuários compartilham um estoque de memória, há o risco de informações transbordarem entre os contextos.</p><p>Por exemplo, se os dados de embeddings de dois usuários diferentes residirem no mesmo banco de dados vetorial sem o devido controle de acesso, um agente que responde à consulta do Usuário A pode, acidentalmente, recuperar parte da memória do Usuário B. Esse <strong><em>vazamento entre contextos</em></strong> pode expor informações privadas ou apenas criar confusão nas respostas.</p><p>De acordo com o <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">OWASP Top 10 for LLM Applications</a>, os bancos de dados vetoriais multilocatários devem se proteger contra esse tipo de vazamento:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f0ccf0b2f7bd6715c14aceee2deffb213d50bd9-1600x936.png" style="width: 100%;" /><p>De acordo com <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">LLM08:2025 — Fraquezas em Vetores e Embeddings</a><em>,</em> um dos riscos comuns é o vazamento de contexto:</p><blockquote><em>Em ambientes multi-inquilinos onde várias classes de usuários ou aplicativos compartilham o mesmo banco de dados vetorial, existe o risco de vazamento de contexto entre usuários ou consultas. Erros de conflito de conhecimento na federação de dados podem ocorrer quando dados de múltiplas fontes se contradizem. Isso também pode ocorrer quando um LLM não consegue substituir o conhecimento antigo que aprendeu durante o treinamento pelos novos dados do Retrieval Augmentation.</em></blockquote><p>Outro aspecto é que um LLM pode ter dificuldade em substituir seu <strong>conhecimento integrado</strong> por novas informações da memória. Se o modelo foi treinado em algum fato e o contexto recuperado diz o oposto, o modelo pode ficar confuso sobre qual confiar. Sem um design adequado, o agente pode misturar contextos ou não conseguir atualizar o conhecimento antigo com novas evidências, levando a respostas desatualizadas ou incorretas.</p><h3><strong>Alucinações e desinformação</strong></h3><p>Embora a <em>alucinação </em>(o LLM inventando informações plausíveis, mas falsas) seja um problema conhecido, mesmo sem contextos longos, o gerenciamento inadequado da memória pode amplificá-lo. Você pode ter que se preocupar com o fato de que o LLM não está preparado para isso. </p><p>Se faltar um fato crucial na memória do agente, o modelo pode simplesmente <strong>preencher a lacuna com um palpite</strong> e, se esse palpite entrar no contexto (envenenando-o), o erro persistirá. </p><p>O relatório de segurança OWASP LLM <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/"><strong>(LLM09:2025 — Desinformação)</strong></a> destaca a desinformação como uma vulnerabilidade central: os modelos de aprendizagem de linguagem (LLMs) podem produzir respostas confiantes, porém fabricadas, e os usuários podem confiar excessivamente nelas. Um agente com uma memória de longo prazo ruim ou desatualizada pode citar com segurança algo que era verdadeiro no ano passado, mas é falso agora, a menos que sua memória seja mantida atualizada. </p><p>A dependência excessiva da saída da IA (seja pelo usuário ou pelo próprio agente em um loop) pode piorar isso. Se ninguém nunca conferir as informações na memória, o agente pode acumular falsidades. É por isso que o RAG é frequentemente usado para reduzir alucinações: ao recuperar uma fonte autoritativa, o modelo não precisa inventar fatos. Mas se sua recuperação extrair o documento errado (digamos, um que contenha informações erradas) ou se uma alucinação precoce não for eliminada, o sistema poderá propagar essa desinformação por meio de suas ações. </p><p>O resultado final: deixar de gerenciar a memória pode levar a <strong>saídas incorretas e enganosas</strong>, o que pode ser prejudicial, especialmente se os riscos forem altos (por exemplo, conselhos ruins em um domínio financeiro ou médico). Um agente precisa de mecanismos para verificar ou corrigir seu conteúdo de memória, não apenas confiar incondicionalmente em qualquer coisa que esteja no contexto.</p><p>Em resumo, dar a um agente de IA uma memória infinitamente longa ou despejar todas as coisas possíveis em seu contexto <em>não</em> é uma receita para o sucesso.</p><h2>Práticas recomendadas para o gerenciamento de memória em aplicações LLM</h2><p>Para evitar as armadilhas acima, os desenvolvedores e pesquisadores criaram uma série de <strong>práticas recomendadas para gerenciar o contexto e a memória</strong> nos sistemas de IA. Essas práticas visam manter o contexto de trabalho da IA enxuto, relevante e atualizado. Aqui estão algumas das principais estratégias, além de exemplos de como elas ajudam.</p><h3>RAG: use contexto direcionado</h3><p>Grande parte do conceito RAG já foi abordada na seção anterior, portanto, este texto serve como um conjunto conciso de lembretes práticos:</p><ul><li>Use recuperação direcionada, não carregamento em massa: recupere apenas os trechos mais relevantes em vez de enviar documentos inteiros ou históricos completos de conversas para o prompt.</li><li>Considere o RAG como uma recuperação de memória sob demanda: busque o contexto apenas quando necessário, em vez de carregar tudo adiante entre as interações.</li><li>Prefira estratégias de recuperação com base na relevância: abordagens como busca semântica top k, fusão de classificação recíproca ou filtragem de carga de ferramentas ajudam a reduzir o ruído e melhorar o aterramento.</li><li>Janelas de contexto maiores não eliminam a necessidade do RAG: dois parágrafos altamente relevantes quase sempre são mais eficazes do que 20 páginas vagamente relacionadas.</li></ul><p>Dito isso, o RAG não se trata de adicionar mais contexto; trata-se de adicionar o contexto certo.</p><h3>Configuração de ferramentas</h3><p>O <em>carregamento de ferramentas</em> consiste em fornecer a um modelo apenas as ferramentas de que ele realmente precisa para uma tarefa. O termo vem de jogos: você escolhe um carregamento que se adequa à situação. Muitas ferramentas atrasam você; as erradas causam falhas. LLMs se comportam da mesma forma, segundo o artigo <a href="https://arxiv.org/abs/2411.15399">Menos é mais</a>. Depois que você passa por ~30 ferramentas, as descrições começam a se sobrepor e o modelo fica confuso. Depois de ~100 ferramentas, o fracasso é quase garantido. Isso não é um problema de janela de contexto, é confusão de contexto.</p><p>Uma solução simples e eficaz é o <a href="https://arxiv.org/abs/2505.03275"><strong>RAG-MCP</strong></a>. Em vez de inserir todas as ferramentas no prompt, as descrições das ferramentas são armazenadas em um banco de dados vetorial e somente as mais relevantes são recuperadas por solicitação. Na prática, isso mantém o carregamento pequeno e focado, encurta drasticamente os prompts e pode melhorar a precisão da seleção de ferramentas em até 3 vezes.</p><p>Modelos menores batem nessa barreira ainda mais cedo. A pesquisa mostra que um modelo 8B falha com dezenas de ferramentas, mas tem sucesso assim que o equipamento é cortado. Selecionar ferramentas dinamicamente, às vezes primeiro com um LLM, raciocinar sobre o que ele acha que precisa, pode aumentar o desempenho em 44%, além de reduzir o consumo de energia e a latência. A conclusão é que a maioria dos agentes precisa apenas de algumas ferramentas, mas à medida que seu sistema cresce, o carregamento de ferramentas e o RAG-MCP se tornam decisões de design de primeira ordem.</p><h3>Eliminação de contexto: limite o tempo do histórico de chat</h3><p>Se uma conversa continuar por várias interações, o histórico de chat acumulado pode ficar muito extenso, causando um excesso de contexto ou distraindo demais o modelo. </p><p><em>Aparar</em> significa remover ou encurtar programaticamente as partes menos importantes do diálogo à medida que ele cresce. Uma forma simples é eliminar as interações mais antigas da conversa quando você atingir um determinado limite, mantendo apenas as <em>N</em> mensagens mais recentes. Uma eliminação mais sofisticada pode remover digressões irrelevantes ou instruções anteriores que não são mais necessárias. O objetivo é <strong>manter a janela de contexto desobstruída</strong> de notícias antigas. </p><p>Por exemplo, se o agente resolveu um subproblema há 10 interações e nós seguimos em frente desde então, podemos excluir essa parte do histórico do contexto (assumindo que não será mais necessário). Muitas implementações baseadas em chat fazem isso: elas mantêm uma janela móvel de mensagens recentes. </p><p>Aparar pode ser tão simples quanto &quot;esquecer&quot; as partes mais iniciais de uma conversa depois que elas foram resumidas ou consideradas irrelevantes. Ao fazer isso, reduzimos o risco de erros de excesso de contexto e também diminuímos a <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-distraction"><strong>distração do contexto</strong></a>, para que o modelo não veja e se distraia com conteúdo antigo ou fora do tema. Essa abordagem é muito parecida com como os humanos podem não lembrar cada palavra de uma conversa de uma hora, mas manterão os destaques. </p><p>Se você está confuso sobre a eliminação de contexto, como destacado pelo autor Drew Breunig <a href="https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#tool-loadout:~:text=Provence%20is%20fast%2C%20accurate%2C%20simple%20to%20use%2C%20and%20relatively%20small%20%E2%80%93%20only%201.75%20GB.%20You%20can%20call%20it%20in%20a%20few%20lines%2C%20like%20so%3A">aqui</a>, o uso do modelo Provence (`<a href="https://huggingface.co/naver/provence-reranker-debertav3-v1">naver/provence-reranker-debertav3-v1`</a>), um eliminador de contexto leve (1,75 GB), eficiente e preciso para resposta a perguntas, pode fazer a diferença. Ele pode reduzir documentos grandes apenas para o texto mais relevante para uma determinada consulta. Você pode chamá-lo em intervalos específicos.</p><p>Veja como invocamos o modelo &#x27;provence-reranker&#x27; em nosso código para eliminar o contexto:</p><pre><code># Context pruning with Provence
def prune_with_provence(query: str, context: str, threshold: Optional[float] = None) -> str:
    """
    Prune context using Provence reranker model
    
    Args:
        query: User's query/question
        context: Original context to prune
        threshold: Relevance threshold (0-1) for Provence reranker.
                   If None, uses args.pruning_threshold.
                   0.1 = conservative (recommended, no performance drop)
                   0.3-0.5 = moderate to aggressive pruning
    
    Returns:
        Pruned context with only relevant sentences
    """
    if provence_model is None:
        return context
    
    if threshold is None:
        threshold = args.pruning_threshold
    
    try:
        # Use Provence's process method
        provence_output = provence_model.process(
            question=query,
            context=context,
            threshold=threshold,
            always_select_title=False,
            enable_warnings=False
        )
        
        # Extract pruned context from output
        pruned_context = provence_output.get('pruned_context', context)
        reranking_score = provence_output.get('reranking_score', 0.0)
        
        # Log statistics
        original_length = len(context)
        pruned_length = len(pruned_context)
        reduction_pct = ((original_length - pruned_length) / original_length * 100) if original_length > 0 else 0
        
        if args.verbose:
            rich.print(f"[cyan]📊 Pruning stats: {pruned_length}/{original_length} chars ({reduction_pct:.1f}% reduction, threshold={threshold:.2f}, rerank_score={reranking_score:.3f})[/cyan]")
        
        return pruned_context if pruned_context else context
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in Provence pruning: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context</code></pre><p>Usamos o modelo Provence reranker (`naver/provence-reranker-debertav3-v1`) para pontuar a relevância da frase. A filtragem baseada em limiar mantém as sentenças acima do limiar de relevância. Além disso, introduzimos um mecanismo de fallback, no qual retornamos ao contexto original se a eliminação falhar. Por fim, o logging de estatísticas acompanha a porcentagem de redução em modo detalhado.</p><h3>Resumo do contexto: condense informações antigas em vez de descartá-las completamente</h3><p>O <em>resumo</em> é um complemento para redução. Quando a história ou a base de conhecimento se tornar muito grande, você pode usar o LLM para gerar um breve resumo dos pontos importantes e usar esse resumo no lugar do conteúdo completo daqui para frente, como fizemos no código acima.</p><p>Por exemplo, se um assistente de IA tiver tido uma conversa de 50 interações, em vez de enviar todas as 50 interações para o modelo na interação 51 (o que provavelmente não caberia), o sistema poderia pegar as interações de 1 a 40, fazer com que o modelo as resumisse em um parágrafo e, em seguida, fornecer apenas esse resumo mais as 10 últimas interações na próxima solicitação. Dessa forma, o modelo ainda sabe o que foi discutido sem precisar de todos os detalhes. Os primeiros usuários de chatbots faziam isso manualmente perguntando: &quot;Você pode resumir o que discutimos até agora?&quot; e então continuando em uma nova sessão com o resumo. Agora isso pode ser automatizado. O resumo não apenas economiza espaço na janela de contexto, mas também pode reduzir <strong>a confusão/distração do contexto</strong> ao eliminar detalhes extras e reter apenas os fatos mais importantes.</p><p>Veja como usamos modelos OpenAI (você pode usar qualquer LLMs) para condensar o contexto preservando todas as informações relevantes, eliminando redundância e duplicação.<br/></p><pre><code># Context summarization
def summarize_context(query: str, context: str) -> str:
    """
    Summarize context using LLM to reduce duplication and focus on relevant information
    
    Args:
        query: User's query/question
        context: Context to summarize
        
    Returns:
        Summarized context
    """
    try:
        summary_prompt = f"""You are an expert at summarizing conversation context.

Your task: Analyze the provided conversation context and produce a condensed summary that fully answers or supports the user's specific question.

The summary must:
1. Preserve every fact, detail, and information that directly relates to the question
2. Eliminate redundancy and duplicate information
3. Maintain chronological flow when relevant
4. Focus on information that helps answer: "{query}"

Context to summarize:
{context}

Provide a concise summary that preserves all relevant information:"""

        summary = llm.invoke(summary_prompt).content
        
        if args.verbose:
            original_length = len(context)
            summary_length = len(summary)
            reduction_pct = ((original_length - summary_length) / original_length * 100) if original_length > 0 else 0
            rich.print(f"[cyan]📝 Summarization stats: {summary_length}/{original_length} chars ({reduction_pct:.1f}% reduction)[/cyan]")
        
        return summary
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in context summarization: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context</code></pre><p>É importante ressaltar que, quando o contexto é resumido, o modelo tem menos probabilidade de ser sobrecarregado por detalhes triviais ou erros passados (presumindo que o resumo seja preciso). </p><p>No entanto, o resumo deve ser feito com cuidado. Um resumo ruim pode omitir um detalhe crucial ou até mesmo introduzir um erro. É basicamente mais um prompt para o modelo (&quot;resuma isso&quot;), então ele pode alucinar ou perder nuances. A melhor prática é resumir de forma incremental e talvez manter alguns fatos canônicos sem resumo.</p><p>Ainda assim, tem se mostrado muito útil. <a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">No cenário do agente Gemini, </a>resumir o contexto a cada ~100k tokens era uma forma de combater a tendência do modelo de se repetir. O resumo funciona como uma memória comprimida da conversa ou dos dados. Como desenvolvedores, podemos implementar isso fazendo com que um agente chame periodicamente uma função de resumo (talvez um LLM menor ou uma rotina dedicada) no histórico de conversas ou em um documento longo. O resumo resultante substitui o conteúdo original no prompt. Essa tática é amplamente usada para manter os contextos dentro de limites e destilar as informações.</p><h3>Quarentena de contexto: isole contextos sempre que possível</h3><p>Isso é mais relevante em sistemas de agentes complexos ou fluxos de trabalho de múltiplas etapas. A ideia da segmentação de contexto é dividir uma grande tarefa em tarefas menores e isoladas, cada uma com seu próprio contexto, de modo que você nunca acumule um contexto enorme que contenha tudo. Cada subagente ou subtarefa trabalha em uma parte do problema com um contexto focado e, em seguida, um agente, supervisor ou coordenador de nível superior integra os resultados.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f2de68c3339883d7658e633af3948f29f427e6cf-1600x900.png" style="width: 100%;" /><p><a href="https://www.anthropic.com/engineering/multi-agent-research-system">A estratégia de pesquisa da Anthropic utiliza múltiplos subagentes</a>, cada um investigando um aspecto diferente de uma questão, com suas próprias janelas de contexto, e um agente líder que lê os resultados sintetizados desses subagentes. Essa abordagem paralela e modular significa que nenhuma janela de contexto única fica excessivamente inchada. Também reduz a chance de mistura de informações irrelevantes, cada tópico permanece no tópico (sem confusão de contexto) e não carrega bagagem desnecessária ao responder sua subpergunta específica. De certa forma, é como seguir fios de pensamento separados que só compartilham seus resultados, não todo o processo de pensamento.</p><p>Em sistemas multiagente, essa abordagem é essencial. Se o Agente A estiver lidando com a tarefa A e o Agente B estiver lidando com a tarefa B, não há motivo para que um dos agentes consuma o contexto completo do outro, a menos que seja realmente necessário. Em vez disso, os agentes podem trocar apenas as informações necessárias. Por exemplo, o Agente A pode passar um resumo consolidado de suas descobertas para o Agente B por meio de um agente supervisor, enquanto cada subagente mantém seu próprio thread de contexto dedicado. Essa configuração não exige supervisão; ela depende de um agente supervisor com ferramentas habilitadas e compartilhamento de contexto mínimo e controlado.</p><p>No entanto, projetar seu sistema de modo que agentes ou ferramentas operem com a sobreposição mínima necessária de contexto pode aumentar muito a clareza e o desempenho. Pense nisso como <strong>microsserviços para IA</strong>, cada componente lida com seu contexto e você passa mensagens entre eles de forma controlada, em vez de um contexto monolítico. Essas práticas recomendadas são frequentemente usadas em combinação. Além disso, isso dá a você a flexibilidade de cortar histórico trivial, resumir mensagens ou conversas antigas importantes, transferir os logs detalhados para o Elasticsearch para contexto de longo prazo e usar a recuperação para trazer de volta qualquer coisa relevante quando necessário.</p><p>Como mencionado <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=While%20some%20models,to%20the%20LLM">aqui</a>, o princípio orientador é que o contexto é um recurso limitado e precioso. Você quer que cada token do prompt ganhe seu valor, ou seja, ele deve contribuir para a qualidade da saída. Se algo na memória não está fazendo sua parte (ou pior, causando confusão ativamente), então deve ser eliminado, resumido ou mantido fora.</p><p>Como desenvolvedores, agora podemos programar o contexto da mesma forma que programamos o código, decidindo quais informações incluir, como formatá-las e quando omiti-las ou atualizá-las. Seguindo essas práticas, podemos fornecer aos agentes LLM o contexto necessário para executar tarefas sem incorrer nas falhas descritas anteriormente. O resultado são agentes que lembram do que deveriam, esquecem o que não precisam e recuperam o que precisam a tempo.</p><h2>Conclusão</h2><p>Memória não é algo que você adiciona a um agente; é algo que você projeta. A memória de curto prazo é o bloco de trabalho do agente, e a memória de longo prazo é seu armazenamento de conhecimento duradouro. O RAG serve de ponte entre os dois, transformando um armazenamento de dados passivo, como o Elasticsearch, em um mecanismo de recuperação ativo que pode ancorar as saídas e manter o agente atualizado.</p><p>Mas a memória é uma faca de dois gumes. No momento em que você deixa o contexto crescer sem controle, você gera envenenamento, distração, confusão e conflitos e, em sistemas compartilhados, até mesmo vazamento de dados. Por isso, o trabalho mais importante com a memória não é &quot;armazenar mais&quot;, mas sim &quot;selecionar melhor&quot;: recuperar seletivamente, eliminar agressivamente, resumir cuidadosamente e evitar misturar contextos não relacionados, a menos que a tarefa realmente o exija.</p><p>Na prática, uma boa engenharia de contexto se assemelha a um bom projeto de sistemas: contextos menores e suficientes, interfaces controladas entre os componentes e uma clara separação entre o estado bruto e o estado refinado que você realmente deseja que o modelo veja. Se feito corretamente, você não acaba com um agente que lembra de tudo — você acaba com um agente que lembra das coisas certas, na hora certa, pelo motivo certo.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Maior taxa de transferência e menor latência: o Elastic Cloud Serverless na AWS recebe um aumento significativo de desempenho]]></title>
            <description><![CDATA[Fizemos um upgrade na infraestrutura da AWS para o Elasticsearch Serverless para hardware mais novo e rápido. Saiba como esse enorme aumento no desempenho gera consultas mais rápidas, melhor redimensionamento e custos mais baixos.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-serverless-aws-performance-boost</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-serverless-aws-performance-boost</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Pete Galeotti, Yuvraj Gupta, Rachel Forshee]]></dc:creator>
            <pubDate>Wed, 14 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aa0b481994d2445ba979d3c79fff64c5ee6676a-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Elastic Cloud Serverless já é a solução definitiva para desenvolvedores que querem criar aplicações eficientes de busca e IA sem o ônus operacional de gerenciar a infraestrutura. Agora, estamos levando o desempenho dos seus projetos sem servidor a um novo patamar.</p><p>Concluímos um grande upgrade na infraestrutura de todos os projetos <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> executados na AWS, migrando para hardware mais novo e mais rápido. Essa mudança foi implementada automaticamente em todos os projetos sem servidor. Ele oferece <strong>maior taxa de transferência e menor latência</strong> em projetos sem servidor com Elasticsearch, Elastic Observability e Elastic Security na AWS.</p><h2><strong>Principais benefícios de desempenho para desenvolvedores</strong></h2><p>A nova infraestrutura de hardware da AWS sustenta tudo o que você faz com o Elastic Cloud Serverless, traduzindo-se em benefícios tangíveis para a velocidade e a capacidade de resposta das suas aplicações.</p><h3><strong>Latência reduzida nas consultas… aumento no rendimento</strong></h3><p>O hardware aprimorado aumenta drasticamente a velocidade dos recursos de computação, o que significa que suas consultas de busca são processadas mais rápido do que nunca.</p><ul><li><strong>Busca e busca vetorial:</strong> seja executando consultas de texto completo tradicionais ou usando a busca vetorial de ponta para suas <a href="https://www.elastic.co/generative-ai">aplicações de IA generativa e retrieval-augmented generation (RAG)</a>, você verá uma redução significativa na latência. O benchmarking interno mostrou uma redução média de 35% na latência de busca.</li><li><strong>Indexação mais rápida:</strong> as taxas de ingestão de dados são otimizadas, permitindo que você indexe volumes massivos de dados e documentos complexos com maior taxa de throughput. Isso é crucial para aplicações que exigem visibilidade de dados em quase tempo real. Benchmarking interno mostrou um aumento médio de 26% na taxa de throughput de indexação.</li></ul><h3><strong>Desempenho consistente sob carga</strong></h3><p>O Elastic Cloud Serverless foi projetado para escalar automaticamente de forma dinâmica em tempo real para atender à demanda, minimizando a latência, independentemente da sua carga de trabalho. Com esse upgrade no hardware, esse redimensionamento agora é mais eficiente e responsivo.</p><ul><li><strong>Lidando com picos com facilidade:</strong> seja enfrentando um aumento repentino no tráfego de usuários ou uma ingestão massiva de dados em lote, a nova infraestrutura garante que seus recursos de busca e indexação sejam redimensionados de forma mais eficiente para manter uma latência consistentemente baixa.</li><li><strong>Desacoplamento otimizado de computação e armazenamento:</strong> a arquitetura sem servidor separa computação e armazenamento, permitindo que cargas de trabalho sejam redimensionadas de forma independente para desempenho ideal e eficiência de custos. O hardware mais rápido aprimora a camada de computação, maximizando a eficiência desse design desacoplado.</li></ul><h2><strong>Por trás do capô: resultados internos de benchmarking</strong></h2><p>Para quantificar o impacto do nosso upgrade de infraestrutura da AWS, a equipe de engenharia da Elastic realizou um benchmarking interno abrangente em relação a uma variedade de cargas de trabalho sem servidor. Essas cargas de trabalho forneceram evidências empíricas de melhorias no desempenho que você pode esperar em todas as suas aplicações, independentemente do seu caso de uso.</p><h3><strong>A abordagem de benchmarking</strong></h3><p>Concentramos nossos testes nas principais métricas que afetam diretamente a experiência do desenvolvedor e a capacidade de resposta da aplicação: tempo de resposta (ou seja, latência) e taxa de transferência em operações de busca e indexação.</p><ul><li><strong>Cargas de trabalho testadas:</strong> os testes incluíram operações de busca de alta concorrência típicas de aplicações voltados para o usuário, consultas de busca vetorial complexas e ingestão/indexação de dados de alto volume para casos de uso de observabilidade e segurança. Em particular, nossa metodologia de teste usou <a href="https://github.com/elastic/rally-tracks/tree/master">publicamente</a> <a href="https://github.com/elastic/rally-tracks/tree/master">conjuntos de dados disponíveis para o Rally</a>, a ferramenta de benchmarking da Elastic.<ul><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/wikipedia"><code>wikipedia</code></a>: Um conjunto de dados derivado de um snapshot do conteúdo textual da Wikipédia, para medir o desempenho em busca de texto de uso geral.</li><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/msmarco-passage-ranking"><code>MSMARCO-Passage-Ranking</code></a>: Um conjunto de dados derivado do Machine Reading Comprehension (MS MARCO) da Microsoft, para medir o desempenho da busca em campos vetoriais esparsos.</li><li><a href="https://github.com/elastic/rally-tracks/tree/3bedd51/openai_vector"><code>OpenAI_Vector</code></a>: Um conjunto de dados derivado do NQ do BEIR e enriquecido com embeddings gerados pelo modelo <code>text-embedding-ada-002</code> da OpenAI, para medir o desempenho da busca em campos vetoriais densos.</li></ul></li><li><strong>Medição:</strong> comparamos o desempenho na infraestrutura antiga e na nova, medindo a latência no 99º percentil (P99) para capturar o pior caso, o desempenho da latência de cauda e as operações por segundo. Cada pista era executada cinco vezes em cada perfil de hardware para garantir consistência nos resultados.</li><li><strong>A meta:</strong> nosso objetivo era validar a capacidade da infraestrutura de fornecer <strong>um desempenho consistentemente mais rápido e previsível</strong> em todos os aspectos, mesmo durante períodos de rápida expansão automática.</li></ul><h3><strong>Resumo de dados de desempenho</strong></h3><p>Os resultados confirmam ganhos significativos em eficiência e velocidade. Essas melhorias se traduzem diretamente em tempos de resposta mais baixos para os seus usuários e custos operacionais mais baixos, como resultado da capacidade de concluir a mesma quantidade de trabalho com menos recursos computacionais.</p><p>As tabelas a seguir detalham as melhorias quantitativas. Valores mais altos são melhores para a throughput; valores mais baixos são melhores para a latência.</p><p><strong>Procurando resultados de benchmarks:</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Benchmark</td><td style="border: 1px solid #ccc; padding: 4px;">Comparação</td><td style="border: 1px solid #ccc; padding: 4px;">Infraestrutura antiga</td><td style="border: 1px solid #ccc; padding: 4px;">Nova infraestrutura</td><td style="border: 1px solid #ccc; padding: 4px;">Diferencial</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`wikipedia` (texto simples)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">729</td><td style="border: 1px solid #ccc; padding: 4px;">1.107</td><td style="border: 1px solid #ccc; padding: 4px;">+52%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`wikipedia` (texto simples)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">56</td><td style="border: 1px solid #ccc; padding: 4px;">35</td><td style="border: 1px solid #ccc; padding: 4px;">-37%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`MSMARCO-Passage-Ranking` (vetores esparsos)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">31</td><td style="border: 1px solid #ccc; padding: 4px;">+40%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`MSMARCO-Passage-Ranking` (vetores esparsos)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">108</td><td style="border: 1px solid #ccc; padding: 4px;">67</td><td style="border: 1px solid #ccc; padding: 4px;">-38%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (vetores densos)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">475</td><td style="border: 1px solid #ccc; padding: 4px;">624</td><td style="border: 1px solid #ccc; padding: 4px;">+31%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (vetores densos)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">35</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">-37%</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Resultados de benchmarks de indexação:</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Benchmark</td><td style="border: 1px solid #ccc; padding: 4px;">Comparação</td><td style="border: 1px solid #ccc; padding: 4px;">Infraestrutura antiga</td><td style="border: 1px solid #ccc; padding: 4px;">Nova infraestrutura</td><td style="border: 1px solid #ccc; padding: 4px;">Diferencial</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`wikipedia` (texto simples)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">2845</td><td style="border: 1px solid #ccc; padding: 4px;">3.220</td><td style="border: 1px solid #ccc; padding: 4px;">+13%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`wikipedia` (texto simples)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">1769</td><td style="border: 1px solid #ccc; padding: 4px;">1.120</td><td style="border: 1px solid #ccc; padding: 4px;">-37%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`MSMARCO-Passage-Ranking` (vetores esparsos)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">7.087</td><td style="border: 1px solid #ccc; padding: 4px;">8.900</td><td style="border: 1px solid #ccc; padding: 4px;">+26%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`MSMARCO-Passage-Ranking` (vetores esparsos)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">824</td><td style="border: 1px solid #ccc; padding: 4px;">677</td><td style="border: 1px solid #ccc; padding: 4px;">-18%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (vetores densos)</td><td style="border: 1px solid #ccc; padding: 4px;">Throughput da operação de buscar (ops/s)</td><td style="border: 1px solid #ccc; padding: 4px;">2972</td><td style="border: 1px solid #ccc; padding: 4px;">3187</td><td style="border: 1px solid #ccc; padding: 4px;">+7%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">`OpenAI_Vector` (vetores densos)</td><td style="border: 1px solid #ccc; padding: 4px;">Latência da operação de busca (p99, ms)</td><td style="border: 1px solid #ccc; padding: 4px;">2.946</td><td style="border: 1px solid #ccc; padding: 4px;">2.944</td><td style="border: 1px solid #ccc; padding: 4px;">0%</td>
                    </tr>
              </tbody>
            </table>
          <h2><strong>O bônus adicional: redução de custos</strong></h2><p>Embora nosso foco seja entregar desempenho de baixa latência, a eficiência do novo hardware também tem um impacto direto e positivo nos custos dos projetos Elasticsearch.</p><p><a href="https://www.elastic.co/pricing/serverless-search">O preço do Elasticsearch Serverless</a> é baseado no uso, o que significa que você paga apenas pelos recursos de ingestão e buscas que você consumir. Como o hardware mais novo e mais rápido é mais eficiente, suas cargas de trabalho geralmente concluem tarefas usando menos recursos, levando a uma redução de custo inerente à maioria dos projetos. Você recebe um aumento de desempenho premium sem o preço premium — a definição de eficiência otimizada.</p><h2><strong>O que isso significa para você, o desenvolvedor?</strong></h2><p>Esse upgrade na infraestrutura é totalmente gerenciado pela Elastic, então você não precisa mexer um dedo — sem migrações e sem mudanças de configuração. A melhoria é imediata e automática em todos os seus projetos sem servidor baseados na AWS.</p><p>Esse upgrade permite que você:</p><ul><li><strong>Construa aplicações mais rápidas:</strong> foque a agilidade no desenvolvimento de recursos, sabendo que sua plataforma de busca está entregando a velocidade que seus usuários exigem.</li><li><strong>Inove com confiança:</strong> implante novos recursos de busca, observabilidade e segurança, incluindo recursos complexos de IA, como busca vetorial e classificação por relevância, com a garantia de que a plataforma possa lidar com a carga em desempenho máximo.</li><li><strong>Simplifique sua pilha:</strong> use um serviço totalmente gerenciado que lide com o gerenciamento da infraestrutura, o planejamento da capacidade e o redimensionamento, para que você possa se concentrar no código e nos dados.<br/></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criação de agentes humanos com o LangGraph e o Elasticsearch]]></title>
            <description><![CDATA[Saiba como criar agentes humanos com LangGraph e Elasticsearch que envolvem pessoas no processo de tomada de decisão para preencher lacunas contextuais e revisar chamadas de ferramentas antes da execução.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/human-in-the-loop-agents-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/human-in-the-loop-agents-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 05 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/69634710b14adff494b51b2d57d119196ea6d644-838x532.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, exploraremos como combinar <a href="https://langchain-ai.github.io/langgraph/">LangGraph</a> e Elasticsearch para criar um aplicativo com interação humana (HITL). Essa abordagem permite que os sistemas de IA envolvam os usuários diretamente no processo de tomada de decisão, tornando as interações mais confiáveis e sensíveis ao contexto. Implementaremos um exemplo prático usando um cenário orientado pelo contexto para demonstrar como os fluxos de trabalho do LangGraph podem se integrar ao Elasticsearch para recuperar dados, lidar com a entrada do usuário e produzir resultados refinados.</p><h2>Requisitos</h2><ul><li>NodeJS versão 18 ou mais recente</li><li>Chave de API da OpenAI</li><li>Implantação do Elasticsearch 8.x+</li></ul><h2>Por que usar o LangGraph para sistemas HITL de produção</h2><p>Em um <a href="https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch">artigo anterior</a>, apresentamos o LangGraph e os benefícios para a construção de um sistema RAG usando LLMs e <a href="https://docs.langchain.com/oss/javascript/langgraph/graph-api#conditional-edges">bordas condicionais</a> para tomar decisões automaticamente e exibir resultados. Às vezes, não queremos que o sistema atue de forma autônoma de ponta a ponta, mas queremos que os usuários selecionem opções e tomem decisões dentro do ciclo de execução. Esse conceito é chamado de <strong><em>&quot;Human in the loop&quot;</em></strong> ou interação humana.</p><h2>Intervenção humana</h2><p>Esse é um conceito de IA que permite que uma pessoa real interaja com sistemas de IA para fornecer mais contexto, avaliar respostas, editar respostas, solicitar mais informações etc. Isso é muito útil em cenários de baixa tolerância a erros, como conformidade, tomada de decisões ou geração de conteúdo, ajudando a melhorar a confiabilidade das saídas do LLM.</p><p>Um exemplo comum é quando seu assistente de programação pede permissão para executar um determinado comando no terminal ou mostra o processo de pensamento passo a passo para você aprovar antes de começar a programar.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4df9fe7383f0dc665a1b0866651f4f6a40a14aa8-499x215.png" style="width: 100%;" /><h2>Elasticsearch + LangGraph: Como eles interagem</h2><p>O <a href="https://www.langchain.com/">LangChain</a> permite usar o Elasticsearch como um <a href="https://js.langchain.com/docs/integrations/vectorstores/elasticsearch/">repositório de vetores</a> e executar consultas em aplicações LangGraph, o que é útil para realizar buscas de texto completo ou semânticas, enquanto o LangGraph é usado para definir o fluxo de trabalho, as ferramentas e as interações específicas. Além disso, adiciona a HITL (interação humana) como uma camada adicional de interação com o usuário.</p><h2>Implementação prática: intervenção humana</h2><p>Vamos imaginar que um advogado tenha uma pergunta sobre um caso que ele assumiu recentemente. Sem as ferramentas certas, ele precisaria buscar manualmente artigos legais e precedentes, ler tudo na íntegra e interpretar como eles se aplicam à situação. Com o LangGraph e o Elasticsearch, no entanto, podemos criar um sistema que busca em um banco de dados precedentes legais e gera uma análise do caso que incorpora os detalhes específicos e o contexto fornecido pelo advogado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e95bf4ce115cbe9954ee5033776408bed485b8d2-1600x906.png" style="width: 100%;" /><p>O fluxo de trabalho começa quando o advogado envia uma dúvida jurídica. O sistema realiza uma busca vetorial no Elasticsearch, recupera os precedentes mais relevantes e os apresenta para o advogado escolher usando linguagem natural. Após a seleção, o LLM gera um rascunho de análise e verifica se as informações estão completas. Nesse ponto, o fluxo de trabalho pode seguir dois caminhos: se tudo estiver claro, ele prossegue diretamente para gerar uma análise final; caso contrário, ele faz uma pausa para solicitar esclarecimentos ao advogado. Assim que o contexto em falta for fornecido, o sistema completa a análise e a retorna, levando em consideração os esclarecimentos.</p><p>A seguir, você verá um gráfico elaborado pelo LangGraph que mostra como o app ficará no final do desenvolvimento. Cada nó representa uma ferramenta ou funcionalidade:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fcdc0dd53c436afc8e575d1f8fd3705b4dfe9a32-262x753.png" style="width: 100%;" /><h3>Conjunto de dados</h3><p>Aqui está o <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataset.json">conjunto de dados</a> que será usado para este exemplo. Este conjunto de dados contém uma coleção de precedentes legais, cada um descrevendo um caso envolvendo atrasos no serviço, o raciocínio do tribunal e o resultado.</p><pre><code>[
  {
    "pageContent": "Legal precedent: Case B - Service delay not considered breach. A consulting contract used term 'timely delivery' without specific dates. A three-week delay occurred but contract lacked explicit schedule. Court ruled no breach as parties had not defined concrete timeline and delay did not cause demonstrable harm.",
    "metadata": {
      "caseId": "CASE-B-2022",
      "contractType": "consulting agreement",
      "delayPeriod": "three weeks",
      "outcome": "no breach found",
      "reasoning": "no explicit deadline defined, no demonstrable harm",
      "keyTerms": "timely delivery, open terms, schedule definition",
      "title": "Case B: Delay Without Explicit Schedule"
    }
  },
  ...
]</code></pre><h3>Ingestão e configuração do índice</h3><p>A configuração do índice e a lógica de ingestão de dados são definidas no arquivo <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts</a>, onde declaramos funções para lidar com a criação do índice. Essa configuração é compatível com a <a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">interface de armazenamento vetorial LangChain para Elasticsearch</a>.</p><p><em>Atenção: a configuração do mapeamento também está incluída no arquivo <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts</a>.</em></p><h3>Instale pacotes e configure variáveis de ambiente</h3><p>Vamos iniciar um projeto <a href="https://nodejs.org/en">Node.js</a> com as configurações padrão:</p><ul><li><strong>@elastic/elasticsearch: </strong>cliente Elasticsearch para Node.js. Usado para conectar, criar índices e executar consultas.</li><li><strong>@langchain/community:</strong> oferece integrações para ferramentas compatíveis com a comunidade, incluindo o ElasticVectorSearch.</li><li><strong>@langchain/core:</strong> blocos núcleo do LangChain, como chains, prompts e utilitários.</li><li><strong>@langchain/langgraph:</strong> adiciona orquestração baseada em gráficos, permitindo fluxos de trabalho com nós, bordas e gerenciamento de estados.</li><li><strong>@langchain/openai: </strong>oferece acesso aos modelos da OpenAI (LLMs e integrações) por meio do LangChain.</li><li><strong>dotenv:</strong> carrega variáveis de ambiente de um arquivo .env em process.env.</li><li><strong>tsx: </strong>é uma ferramenta útil para executar código TypeScript.</li></ul><p>Execute o seguinte comando no console para instalar todos eles:</p><pre><code>npm install @elastic/elasticsearch @langchain/community @langchain/core @langchain/langgraph @langchain/openai dotenv --legacy-peer-deps && npm install --save-dev tsx</code></pre><p>Crie um arquivo <code>.env</code> para configurar as variáveis de ambiente:</p><pre><code>ELASTICSEARCH_ENDPOINT=
ELASTICSEARCH_API_KEY=
OPENAI_API_KEY=</code></pre><p>Usaremos o <a href="https://www.typescriptlang.org/">TypeScript</a> para escrever o código porque ele oferece uma camada de segurança de tipos e uma melhor experiência para o desenvolvedor. Crie um arquivo TypeScript chamado <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/main.ts">main.ts</a> e insira o código da próxima seção.</p><h3>Importações de pacotes</h3><p>No arquivo <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/main.ts">main.ts</a>, começamos importando os módulos necessários e inicializando a configuração da variável de ambiente. Isso inclui os componentes do núcleo do LangGraph, as integrações do modelo OpenAI e o cliente Elasticsearch.</p><p>Também importamos o seguinte do arquivo <a href="https://github.com/Delacrobix/LangGraph-JS-Elasticsearch-human-in-the-loop/blob/main/dataIngestion.ts">dataIngestion.ts</a>:</p><ul><li><strong>ingestData: </strong>uma função que cria o índice e ingere os dados.</li><li><strong>Document e DocumentMetadata:</strong> interfaces que definem a estrutura do documento do conjunto de dados.</li></ul><h3>Cliente de armazenamento vetorial Elasticsearch, cliente de interações e cliente OpenAI</h3><p>Esse código inicializará o armazenamento vetorial, o cliente de integrações e um cliente OpenAI.</p><pre><code>const VECTOR_INDEX = "legal-precedents";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
const embeddings = new OpenAIEmbeddings({
  model: "text-embedding-3-small",
});

const esClient = new Client({
  node: process.env.ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: process.env.ELASTICSEARCH_API_KEY ?? "",
  },
});

const vectorStore = new ElasticVectorSearch(embeddings, {
  client: esClient,
  indexName: VECTOR_INDEX,
});</code></pre><p>O esquema de estado do fluxo de trabalho da aplicação ajudará na comunicação entre os nós:</p><pre><code>const LegalResearchState = Annotation.Root({
  query: Annotation<string>(),
  analyzedConcepts: Annotation<string[]>(),
  precedents: Annotation<Document[]>(),
  selectedPrecedent: Annotation<Document | null>(),
  draftAnalysis: Annotation<string>(),
  ambiguityDetected: Annotation<boolean>(),
  userClarification: Annotation<string>(),
  finalAnalysis: Annotation<string>(),
});</code></pre><p>No objeto de estado, passaremos a consulta do usuário, os conceitos extraídos dela, os precedentes legais recuperados e qualquer ambiguidade detectada sempre pelos nós. O estado também rastreia o precedente selecionado pelo usuário, o rascunho de análise gerado ao longo do caminho e a análise final quando todos os esclarecimentos forem concluídos.</p><h3>Nós</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9bda47908dc0a73fdda222c8ba1382a1ef9dc77a-304x300.png" style="width: 100%;" /><p><strong>searchPrecedents: </strong>Este nó realiza uma busca por similaridade no armazenar vetorial do Elasticsearch baseada na entrada do usuário Ele recupera até 5 documentos correspondentes e os imprime para que possam ser revisados pelo usuário.</p><pre><code>async function searchPrecedents(state: typeof LegalResearchState.State) {
  console.log(
    "📚 Searching for relevant legal precedents with query:\n",
    state.query
  );

  const results = await vectorStore.similaritySearch(state.query, 5);
  const precedents = results.map((d) => d as Document);

  console.log(`Found ${precedents.length} relevant precedents:\n`);

  for (let i = 0; i < precedents.length; i++) {
    const p = precedents[i];
    const m = p.metadata;
    console.log(
      `${i + 1}. ${m.title} (${m.caseId})\n` +
        `   Type: ${m.contractType}\n` +
        `   Outcome: ${m.outcome}\n` +
        `   Key reasoning: ${m.reasoning}\n` +
        `   Delay period: ${m.delayPeriod}\n`
    );
  }

  return { precedents };
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82afb46c4373b01daf7640a528c2ea54db0837b5-304x300.png" style="width: 100%;" /><p><strong>precedentSelection:</strong> este nó permite ao usuário selecionar, usando linguagem natural, o caso de uso recuperado pela pesquisa com a proximidade que melhor corresponde à pergunta. Nesse ponto, o aplicativo<strong> interrompe o fluxo de trabalho</strong> e aguarda a entrada do usuário.</p><pre><code>function precedentSelection(state: typeof LegalResearchState.State) {
  console.log("\n⚖️  HITL #1: Human input needed\n");
  const question = "👨‍⚖️  Which precedent is most similar to your case? ";
  const userChoice = interrupt({ question });

  return { userChoice };
}</code></pre><p><strong>selectPrecedent:</strong> este no envia a entrada do usuário, juntamente com os documentos recuperados, para serem interpretados de forma que um deles possa ser selecionado. O LLM realiza essa tarefa retornando um número que representa o documento que ele infere a partir da entrada em linguagem natural do usuário.</p><pre><code>async function selectPrecedent(state: typeof LegalResearchState.State) {
  const precedents = state.precedents || [];
  const userInput = (state as any).userChoice || "";

  const precedentsList = precedents
    .map((p, i) => {
      const m = p.metadata;
      return `${i + 1}. ${m.caseId}: ${m.title} - ${m.outcome}`;
    })
    .join("\n");

  const structuredLlm = llm.withStructuredOutput({
    name: "precedent_selection",
    schema: {
      type: "object",
      properties: {
        selected_number: {
          type: "number",
          description:
            "The precedent number selected by the lawyer (1-based index)",
          minimum: 1,
          maximum: precedents.length,
        },
      },
      required: ["selected_number"],
    },
  });

  const prompt = `
    The lawyer said: "${userInput}"

    Available precedents:
    ${precedentsList}

    Which precedent number (1-${precedents.length}) matches their selection?
  `;

  const response = await structuredLlm.invoke([
    {
      role: "system",
      content:
        "You are an assistant that interprets lawyer's selection and returns the corresponding precedent number.",
    },
    { role: "user", content: prompt },
  ]);

  const selectedIndex = response.selected_number - 1;
  const selectedPrecedent = precedents[selectedIndex] || precedents[0];

  console.log(`✅ Selected: ${selectedPrecedent.metadata.title}\n`);
  return { selectedPrecedent };
}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/714e840cc8dab48c436cd066a6fdb4b7dceccf13-428x260.png" style="width: 100%;" /><p><strong>createDraft: </strong>este nó gera a análise legal inicial com base no precedente selecionado pelo usuário. Ele usa um LLM para avaliar como o precedente escolhido se aplica à pergunta do advogado e determina se o sistema tem informações suficientes para prosseguir.</p><p>Se o precedente puder ser aplicado diretamente, o nó produz uma análise preliminar e, seguindo o caminho correto, salta para o nó final. Se o LLM detectar ambiguidades, como termos contratuais indefinidos, detalhes do cronograma ausentes ou condições pouco claras, ele retorna com uma bandeira indicando que é necessário esclarecimento, junto com uma lista das informações específicas que devem ser fornecidas. Neste caso, a ambiguidade desencadeia o caminho à esquerda do gráfico.</p><pre><code>async function createDraft(state: typeof LegalResearchState.State) {
  console.log("📝 Drafting initial legal analysis...\n");

  const precedent = state.selectedPrecedent;
  if (!precedent) return { draftAnalysis: "" };

  const m = precedent.metadata;

  const structuredLlm = llm.withStructuredOutput({
    name: "draft_analysis",
    schema: {
      type: "object",
      properties: {
        needs_clarification: {
          type: "boolean",
          description:
            "Whether the analysis requires clarification about contract terms or context",
        },
        analysis_text: {
          type: "string",
          description: "The draft legal analysis or the ambiguity explanation",
        },
        missing_information: {
          type: "array",
          items: { type: "string" },
          description:
            "List of specific information needed if clarification is required (empty if no clarification needed)",
        },
      },
      required: ["needs_clarification", "analysis_text", "missing_information"],
    },
  });

  const prompt = `
    Based on this precedent:
    Case: ${m.title}
    Outcome: ${m.outcome}
    Reasoning: ${m.reasoning}
    Key terms: ${m.keyTerms}

    And the lawyer's question: "${state.query}"

    Draft a legal analysis applying this precedent to the question.
    
    If you need more context about the specific contract terms, timeline details, 
    or other critical information to provide accurate analysis, set needs_clarification 
    to true and list what information is missing.
    
    Otherwise, provide the legal analysis directly.
  `;

  const response = await structuredLlm.invoke([
    {
      role: "system",
      content:
        "You are a legal research assistant that analyzes cases and identifies when additional context is needed.",
    },
    { role: "user", content: prompt },
  ]);

  let displayText: string;
  if (response.needs_clarification) {
    const missingInfoList = response.missing_information
      .map((info: string, i: number) => `${i + 1}. ${info}`)
      .join("\n");
    displayText = `AMBIGUITY DETECTED:\n${response.analysis_text}\n\nMissing information:\n${missingInfoList}`;
  } else {
    displayText = `ANALYSIS:\n${response.analysis_text}`;
  }

  console.log(displayText + "\n");

  return {
    draftAnalysis: displayText,
    ambiguityDetected: response.needs_clarification,
  };
}</code></pre><p>Os dois caminhos que o gráfico pode seguir são os seguintes:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/72dda52e1d84818c9f98437f2fa6ecb8e5b2f4ff-438x638.png" style="width: 100%;" /><p>O caminho à esquerda inclui um nó adicional que cuida da clarificação.</p><p><strong>requestClarification:</strong> este nó aciona a segunda etapa de intervenção humana quando o sistema identifica que a análise preliminar precisa de contexto essencial. O fluxo de trabalho é interrompido e é solicitado o usuário que ele esclareça os detalhes do contrato ausentes detectados pelo nó anterior.</p><pre><code>function requestClarification(state: typeof LegalResearchState.State) {
  console.log("\n⚖️  HITL #2: Additional context needed\n");
  const userClarification = interrupt({
    question: "👨‍⚖️  Please provide clarification about your contract terms:",
  });
  return { userClarification };
}</code></pre><p><strong>generateFinalAnalysis: </strong>este nó gera a análise jurídica final combinando o precedente selecionado com o contexto adicional fornecido pelo usuário, se necessário. Utilizando os esclarecimentos obtidos na etapa HITL anterior, o LLM sintetiza o raciocínio do precedente, os detalhes do contrato fornecidos pelo usuário e as condições que determinam se pode ter ocorrido uma violação.</p><p>O nó gera uma análise completa que integra interpretação jurídica e recomendações práticas.</p><pre><code>async function generateFinalAnalysis(state: typeof LegalResearchState.State) {
  console.log("📋 Generating final legal analysis...\n");

  const precedent = state.selectedPrecedent;
  if (!precedent) return { finalAnalysis: "" };

  const m = precedent.metadata;

  const prompt = `
    Original question: "${state.query}"
    
    Selected precedent: ${m.title}
    Outcome: ${m.outcome}
    Reasoning: ${m.reasoning}
    
    Lawyer's clarification: "${state.userClarification}"
    
    Provide a comprehensive legal analysis integrating:
    1. The selected precedent's reasoning
    2. The lawyer's specific contract context
    3. Conditions for breach vs. no breach
    4. Practical recommendations
  `;

  const response = await llm.invoke([
    {
      role: "system",
      content:
        "You are a legal research assistant providing comprehensive analysis.",
    },
    { role: "user", content: prompt },
  ]);

  const finalAnalysis = response.content as string;

  console.log(
    "\n" +
      "=".repeat(80) +
      "\n" +
      "⚖️  FINAL LEGAL ANALYSIS\n" +
      "=".repeat(80) +
      "\n\n" +
      finalAnalysis +
      "\n\n" +
      "=".repeat(80) +
      "\n"
  );

  return { finalAnalysis };
}</code></pre><p>Construindo um gráfico:</p><pre><code>const workflow = new StateGraph(LegalResearchState)
  .addNode("analyzeQuery", analyzeQuery)
  .addNode("searchPrecedents", searchPrecedents)
  .addNode("precedentSelection", precedentSelection)
  .addNode("selectPrecedent", selectPrecedent)
  .addNode("createDraft", createDraft)
  .addNode("requestClarification", requestClarification)
  .addNode("generateFinalAnalysis", generateFinalAnalysis)
  .addEdge("__start__", "analyzeQuery")
  .addEdge("analyzeQuery", "searchPrecedents")
  .addEdge("searchPrecedents", "precedentSelection") // HITL #1
  .addEdge("precedentSelection", "selectPrecedent")
  .addEdge("selectPrecedent", "createDraft")
  .addConditionalEdges(
    "createDraft",
    (state: typeof LegalResearchState.State) => {
      // If ambiguity detected, request clarification (HITL #2)
      if (state.ambiguityDetected) return "needsClarification";
      // Otherwise, generate final analysis
      return "final";
    },
    {
      needsClarification: "requestClarification",
      final: "generateFinalAnalysis",
    }
  )
  .addEdge("requestClarification", "generateFinalAnalysis") // HITL #2
  .addEdge("generateFinalAnalysis", "__end__");</code></pre><p>No gráfico, podemos ver que a borda condicional define a condição para a escolha do caminho &quot;final&quot;. Conforme demonstrado, a decisão agora depende do fato de a análise preliminar ter detectado ambiguidade que exija esclarecimentos adicionais.</p><p>Junte tudo para ser executado:</p><pre><code>await ingestData();

// Compile workflow
const app = workflow.compile({ checkpointer: new MemorySaver() });
const config = { configurable: { thread_id: "hitl-circular-thread" } };

await saveGraphImage(app);

// Execute workflow
const legalQuestion =
    "Does a pattern of repeated delays constitute breach even if each individual delay is minor?"; 

console.log(`⚖️  LEGAL QUESTION: "${legalQuestion}"\n`);

let currentState = await app.invoke({ query: legalQuestion }, config);

// Handle all interruptions in a loop
while ((currentState as any).__interrupt__?.length > 0) {
  console.log("\n💭 APPLICATION PAUSED WAITING FOR USER INPUT...");

  const interruptQuestion = (currentState as any).__interrupt__[0]?.value
    ?.question;
  const userChoice = await getUserInput(
    interruptQuestion || "👤 YOUR CHOICE: "
  );

  currentState = await app.invoke(
    new Command({ resume: userChoice }),
    config
  );
}</code></pre><h3>Execute o script:</h3><p>Com todo o código alocado, vamos executar o arquivo <code>main.ts</code> escrevendo o seguinte comando no terminal:</p><pre><code>tsx main.ts</code></pre><p>Após a execução do script, a pergunta <strong>&quot;Um padrão de atrasos repetidos constitui uma violação, mesmo que cada atraso individual seja pequeno?&quot;</strong> será enviada ao Elasticsearch para realizar uma busca por proximidade e os resultados recuperados do índice serão exibidos. O app detecta que vários precedentes relevantes correspondem à consulta, então ele pausa a execução e pede que o usuário ajude a fazer a desambiguação de qual precedente legal for mais aplicável:</p><pre><code>📚 Searching for relevant legal precedents with query:
 Does a pattern of repeated delays constitute breach even if each individual delay is minor?
Found 5 relevant precedents:

1. Case H: Pattern of Repeated Delays (CASE-H-2021)
   Type: ongoing service agreement
   Outcome: breach found
   Key reasoning: pattern demonstrated failure to perform, cumulative effect
   Delay period: multiple instances

2. Case E: Minor Delay Quality Maintained (CASE-E-2022)
   Type: service agreement
   Outcome: minor breach only
   Key reasoning: delay minimal, quality maintained, termination unjustified
   Delay period: five days

3. Case A: Delay Breach with Operational Impact (CASE-A-2023)
   Type: service agreement
   Outcome: breach found
   Key reasoning: delay affected operations and caused financial harm
   Delay period: two weeks

4. Case B: Delay Without Explicit Schedule (CASE-B-2022)
   Type: consulting agreement
   Outcome: no breach found
   Key reasoning: no explicit deadline defined, no demonstrable harm
   Delay period: three weeks

5. Case C: Justified Delay External Factors (CASE-C-2023)
   Type: construction service
   Outcome: no breach found
   Key reasoning: external factors beyond control, force majeure applied
   Delay period: one month

⚖️  HITL #1: Human input needed

💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Which precedent is most similar to your case? </code></pre><p>O interessante sobre este aplicativo é que podemos usar linguagem natural para escolher uma opção, permitindo que o LLM interprete a entrada do usuário para determinar a escolha correta. Vamos ver o que acontece se inserirmos o texto: <strong>“Caso H”</strong></p><pre><code>💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Which precedent is most similar to your case? Case H

✅ Selected: Case H: Pattern of Repeated Delays

📝 Drafting initial legal analysis...

AMBIGUITY DETECTED:
Based on Case H, a pattern of repeated delays can indeed constitute a breach of contract, even if each individual delay is minor. The outcome in Case H indicates that the cumulative effect of these minor delays led to a significant failure to perform the contractual obligations adequately. The reasoning emphasizes that consistent performance is critical in fulfilling the terms of a contract. Therefore, if the repeated delays create a situation where the overall performance is hindered, this pattern could be interpreted as a breach. However, the interpretation may depend on the specific terms of the contract at issue, as well as the expectations of performance set forth in that contract.

Missing information:
1. Specific contract terms regarding performance timelines
2. Details on the individual delays (duration, frequency)
3. Context on consequences of delays stated in the contract
4. Other parties' expectations or agreements related to performance


⚖️  HITL #2: Additional context needed


💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Please provide clarification about your contract terms:</code></pre><p>O modelo pega o esclarecimento do usuário e o integra ao fluxo de trabalho, prosseguindo com a análise final quando o contexto suficiente for fornecido. Nesta etapa, o sistema também utiliza a ambiguidade previamente detectada: a análise preliminar destacou detalhes contratuais ausentes que poderiam afetar e muito a interpretação jurídica. Esses itens de “informações ausentes” orientam o modelo na determinação de quais esclarecimentos são essenciais para resolver a incerteza antes de produzir uma opinião final confiável.</p><p>O usuário deve incluir na próxima entrada as solicitações de esclarecimentos. Vamos tentar com <strong>&quot;O contrato exige &#x27;pronta entrega&#x27; sem cronogramas. 8 atrasos de 2 a 4 dias em 6 meses. US$ 50 mil em perdas devido a 3 prazos não cumpridos pelo cliente. O fornecedor foi notificado, mas o padrão continuou.&quot;</strong></p><pre><code>💭 APPLICATION PAUSED WAITING FOR USER INPUT...
👨‍⚖️  Please provide clarification about your contract terms: Contract requires "prompt delivery" without timelines. 8 delays of 2-4 days over 6 months. $50K in losses from 3 missed client deadlines. Vendor notified but pattern continued.

📋 Generating final legal analysis...

================================================================================
⚖️  FINAL LEGAL ANALYSIS
================================================================================

To analyze the question of whether a pattern of repeated minor delays constitutes a breach of contract, we need to combine insights from the selected precedent, the specifics of the lawyer's contract situation, conditions that typically govern breach versus non-breach, and practical recommendations for the lawyer moving forward.

### 1. Selected Precedent's Reasoning

The precedent case, referred to as Case H, found that a pattern of repeated delays amounted to a breach of contract. The court reasoned that even minor individual delays, when considered cumulatively, demonstrated a failure to perform as stipulated in the contract. The underlying rationale was that the cumulative effect of these minor delays could significantly undermine the purpose of the contract, which typically aims for timely performance and reliable delivery.

### 2. Lawyer's Specific Contract Context

In the lawyer's situation, the contract specified "prompt delivery" but did not provide a strict timeline. The vendor experienced 8 delays ranging from 2 to 4 days over a period of 6 months. These delays culminated in $50,000 in losses due to three missed client deadlines. The vendor was notified regarding these delays; however, the pattern of delays persisted.

Key considerations include:
- **Nature of the Obligations**: While “prompt delivery” does not define a strict timeline, it does imply an expectation for timely performance.
- **Material Impact**: The missed client deadlines indicate that these delays had a material adverse effect on the lawyer's ability to fulfill contractual obligations to third parties, likely triggering damages.

### 3. Conditions for Breach vs. No Breach

**Conditions for Breach**:
- **Pattern and Cumulative Effect**: Similar to the reasoning in Case H, evidence of a habitual pattern of delays can amount to a breach. Even if individual delays are minor, when combined, they may show a lack of diligence or reliability by the vendor.
- **Materiality**: The impact of these delays is crucial. If the cumulative delays adversely affect the contract's purpose or cause significant losses, this reinforces the case for a breach.
- **Notification and Opportunity to Cure**: The fact that the vendor was notified of the delays and failed to rectify the behavior can often be interpreted as a further indication of breach.

**Conditions for No Breach**:
- **Non-Material Delays**: If the delays did not affect the overall contractual performance or client obligations, this may lessen the likelihood of establishing a breach. However, given the risks and losses involved, this seems less relevant in this scenario.
- **Force Majeure or Justifiable Delays**: If the vendor could show that these delays were due to justify circumstances not within their control, it may potentially provide a defense against breach claims.

### 4. Practical Recommendations

1. **Assess Damages**: Document the exact nature of the financial losses incurred due to the missed deadlines to substantiate claims of damages.
  
2. **Gather Evidence**: Collect all communication regarding the delays, including any notifications sent to the vendor about the issues.

3. **Consider Breach of Contract Action**: Based on the precedent and accumulated delays, consider formalized communication to the vendor regarding a breach of contract claim, highlighting both the pattern and the impact of these repeated delays.

4. **Evaluate Remedies**: Depending upon the contract specifics, the lawyer may wish to pursue several remedies, including:
   - **Compensatory Damages**: For the financial losses due to missed deadlines.
   - **Specific Performance**: If timely delivery is critical and can still be enforced.
   - **Contract Termination**: Depending on the severity, terminating the contract and seeking replacements may be warranted.

5. **Negotiate Terms**: If continuing to work with the current vendor is strategic, the lawyer should consider renegotiating terms for performance guarantees or penalties for further delays.

6. **Future Contracts**: In future contracts, consider including explicit timelines and conditions for prompt delivery, as well as specified damages for delays to better safeguard against this issue.

By integrating the legal principles from the precedent with the specific context and conditions outlined, the lawyer can formulate a solid plan to address the repeated delays by the vendor effectively.</code></pre><p>Essa saída mostra a etapa final do fluxo de trabalho, em que o modelo integra o precedente selecionado (Caso H) e os esclarecimentos do advogado para gerar uma análise jurídica completa. O sistema explica por que o padrão de atrasos provavelmente constitui uma violação, destaca os fatores que sustentam essa interpretação e fornece recomendações práticas. No geral, a saída demonstra como os esclarecimentos do HITL resolvem a ambiguidade e permitem que o modelo produza uma opinião jurídica bem fundamentada e específica do contexto.</p><h3>Outros cenários do mundo real</h3><p>Esse tipo de aplicação, usando Elasticsearch, LangGraph e humanos, pode ser útil em outros tipos de apps como:</p><ul><li><strong>Revisando ferramentas de chamadas antes da execução</strong>, por exemplo, em negociações financeiras, um humano aprova pedidos de compra/venda antes que eles sejam feitos.</li><li><strong>Forneça parâmetros adicionais quando necessário</strong>, por exemplo, na triagem de suporte ao cliente, onde um agente humano seleciona a categoria correta de problema quando a IA encontra múltiplas possíveis interpretações do problema do cliente.</li></ul><p>E há muitos casos de uso que precisam ser descobertos em que a intervenção humana será um divisor de águas.</p><h2>Conclusão</h2><p>Com o LangGraph e o Elasticsearch, podemos criar agentes que tomem as próprias decisões e atuem como fluxos de trabalho lineares ou tenham condições de seguir um caminho ou outro. Com a intervenção humana, os agentes podem envolver o usuário real no processo de tomada de decisão para preencher lacunas contextuais e solicitar confirmações em sistemas em que a tolerância a falhas é fundamental.</p><p>Uma das vantagens dessa abordagem é que você pode filtrar um grande conjunto de dados usando os recursos do Elasticsearch e usar um LLM para ter um único documento como seleção do usuário. Essa última etapa seria muito mais complicada se você usasse apenas o Elasticsearch, pois há muitas maneiras de um ser humano se referir a um resultado usando linguagem natural.</p><p>Essa abordagem mantém o sistema rápido e eficiente em termos de tokens, pois enviamos ao LLM apenas o necessário para tomar a decisão final e não o conjunto de dados completo. Ao mesmo tempo, isso mantém a precisão na detecção da intenção do usuário e permite iterar até que a opção desejada seja escolhida.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Automatização da análise de logs no Streams com ML]]></title>
            <description><![CDATA[Descubra como uma abordagem híbrida de ML alcançou 94% de precisão na análise de logs e 91% na partição de logs por meio de experimentos de automação com impressão digital de formato de log no Streams.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/log-parsing-partitioning-automation-experiments-streams</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/log-parsing-partitioning-automation-experiments-streams</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Nastia Havriushenko]]></dc:creator>
            <pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/965c58f19742361160593c38fcaa8b2f4b0d6cc5-3838x2159.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Nas pilhas modernas de observabilidade, a ingestão de logs não estruturados de diversos provedores de dados em plataformas como o Elasticsearch continua sendo um desafio. A dependência de regras de análise sintática criadas manualmente gera fluxos de trabalho frágeis, onde até mesmo pequenas atualizações no código upstream levam a falhas de análise e dados não indexados. Esta fragilidade é agravada pelo desafio da escalabilidade: em ambientes dinâmicos de microsserviços, a adição contínua de novos serviços transforma a manutenção manual de regras em um pesadelo operacional.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9108ec303339dd091faa3c363c7cf5c228155f49-3840x2160.png" style="width: 100%;" /><p>Nosso objetivo era fazer a transição para uma abordagem automatizada e adaptativa capaz de lidar com a análise de logs (extração de campos) e o particionamento de logs (identificação da fonte). Nossa hipótese é que os grandes modelos de linguagem (LLMs), com a compreensão inerente da sintaxe do código e dos padrões semânticos, poderiam automatizar essas tarefas com o mínimo de intervenção humana.</p><p>Temos o prazer de anunciar que esse recurso já está disponível no <a href="http://elastic.co/elasticsearch/streams"><span style="text-decoration:underline">Streams</span></a>!</p><h2>Descrição do conjunto de dados</h2><p>Escolhemos uma coleção de logs do <strong><a href="https://github.com/logpai/loghub">Loghub</a> </strong>para fins de PoC. Para nossa investigação, selecionamos amostras representativas das seguintes áreas-chave:</p><ul><li>Sistemas distribuídos: utilizamos os conjuntos de dados HDFS (Hadoop Distributed File System) e Spark. Esses contêm uma mistura de informações, mensagens de debug e erros típicos das plataformas de big data.</li><li>Servidores e aplicações web: logs dos servidores web Apache e do OpenSSH forneceram uma fonte valiosa de acesso, erro e eventos relevantes para a segurança. Esses são fundamentais para monitorar o tráfego web e detectar ameaças potenciais.</li><li>Sistemas operacionais: incluímos logs do Linux e do Windows. Esses conjuntos de dados representam os eventos comuns e semiestruturados em nível de sistema que as equipes de operações enfrentam diariamente.</li><li>Sistemas móveis: para garantir que nosso modelo pudesse lidar com logs de ambientes móveis, incluímos o conjunto de dados Android. Esses logs costumam ser extensos e captam uma ampla gama de atividades em nível de aplicação e sistema em dispositivos móveis.</li><li>Supercomputadores: para testar o desempenho em ambientes de computação de alto desempenho (HPC), incorporamos o conjunto de dados BGL (Blue Gene/L), que apresenta logs altamente estruturados com terminologia específica de domínio.</li></ul><p>Uma das principais vantagens da coleção Loghub é que os logs são, em grande parte, não higienizados e não rotulados, espelhando um ambiente de produção real e ruidoso com arquitetura de microsserviços.</p><p>Exemplos de logs:</p><pre><code>[Sun Dec 04 20:34:21 2005] [notice] jk2_init() Found child 2008 in scoreboard slot 6
[Sun Dec 04 20:34:25 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties
[Mon Dec 05 11:06:51 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties
17/06/09 20:10:58 INFO output.FileOutputCommitter: Saved output of task 'attempt_201706092018_0024_m_000083_1138' to hdfs://10.10.34.11:9000/pjhe/test/1/_temporary/0/task_201706092018_0024_m_000083
17/06/09 20:10:58 INFO mapred.SparkHadoopMapRedUtil: attempt_201706092018_0024_m_000083_1138: Committed</code></pre><p>Além disso, criamos um cluster Kubernetes com uma configuração típica de aplicação web + banco de dados para minerar logs extras no domínio mais comum.</p><p>Exemplo de campos de log comuns: carimbo de tempo, nível de log (INFO, AVISO, ERRO), origem, mensagem.</p><h2>Análise de logs com poucos exemplos usando um LLM</h2><p>Nosso primeiro conjunto de experimentos concentrou-se em uma questão fundamental: <strong>Um LLM pode identificar áreas-chave de forma confiável e gerar regras consistentes de análise para extraí-las?</strong></p><p>Solicitamos a um modelo que analisasse amostras de registros brutos e gerasse regras de análise sintática de log nos formatos de expressão regular (regex) e <a href="https://www.elastic.co/docs/explore-analyze/scripting/grok">Grok</a>. Nossos resultados mostraram que essa abordagem tem muito potencial, mas também apresenta desafios significativos de implementação.</p><h3>Alto nível de confiança e consciência contextual</h3><p>Os resultados iniciais foram promissores. O LLM demonstrou uma forte habilidade de gerar regras de análise sintática que correspondiam aos exemplos de poucos disparos fornecidos com alta confiança. Além da simples correspondência de padrões, o modelo demonstrou capacidade de compreensão de logs, pois ele conseguiu identificar e nomear corretamente a fonte do log (por exemplo, aplicativo de monitoramento de saúde, aplicativo web Nginx, banco de dados MongoDB).</p><h3>O dilema &quot;Cachinhos Dourados&quot; das amostras de entrada</h3><p>Nossos experimentos logo revelaram uma falta significativa de robustez devido à extrema<strong> sensibilidade à amostra de entrada.</strong> O desempenho do modelo varia muito com base nos exemplos específicos de logs incluídos no prompt. Observamos um problema de similaridade de log, onde a amostra de logs precisa incluir <em>logs diversos: </em></p><ul><li>Homogeneidade excessiva (sobreajuste)<strong>:</strong> se os logs de entrada forem muito semelhantes, o LLM tende a <strong>superespecificar</strong>. Ele trata dados de variáveis, como nomes específicos de classes Java em um rastreio de pilha, como partes estáticas do template. Isso resulta em regras frágeis que cobrem uma proporção minúscula de logs e extraem campos inutilizáveis.</li><li>Muito heterogêneo (confusão): por outro lado, se a amostra contiver uma variação significativa de formatação, ou pior, &quot;registros de lixo&quot; como barras de progresso, tabelas de memória ou arte ASCII, o modelo terá dificuldades para encontrar um denominador comum. Geralmente, ele recorre à geração de expressões regulares complexas e quebradas ou à generalização lenta de toda a linha em um único campo blob de mensagem.</li></ul><h3>A restrição da janela de contexto</h3><p>Também encontramos um gargalo na janela de contexto. Quando os registros de entrada eram longos, heterogêneos ou ricos em campos extraíveis, a saída do modelo geralmente se deteriorava, tornando-se &quot;confusa&quot; ou muito longa para caber na janela de contexto de saída. Naturalmente, a fragmentação ajuda nesse caso. Ao dividir os logs usando delimitadores baseados em caracteres e em entidades, podemos ajudar o modelo a se concentrar na extração dos campos principais sem ser sobrecarregado por ruídos.</p><h3>A lacuna de consistência e padronização</h3><p>Mesmo quando o modelo gerou regras com sucesso, notamos pequenas inconsistências:</p><ul><li>Variações de nomenclatura de serviço: o modelo propõe diferentes nomes para a mesma entidade (por exemplo, rotulando a fonte como &quot;Spark&quot;, &quot;Apache Spark&quot; e &quot;Spark Log Analytics&quot; em diferentes execuções).</li><li>Variações na nomenclatura dos campos: os nomes dos campos não tinham padronização (por exemplo, <code>id</code> X <code>service.id</code> X <code>device.id</code>). Normalizamos os nomes usando uma <a href="https://www.elastic.co/docs/reference/ecs/ecs-field-reference">nomenclatura de campo padronizada do Elastic</a>.</li><li>Variância de resolução: a resolução da extração de campo variava dependendo de o quão semelhantes eram os logs de entrada entre si.</li></ul><h2>Formato de log impressão digital</h2><p>Para enfrentar o desafio da similaridade de log, apresentamos uma heurística de alto desempenho: <strong>impressão digital de formato de log (LFF)</strong>.</p><p>Em vez de inserir logs brutos e ruidosos diretamente em um LLM, primeiro aplicamos uma transformação determinística para revelar a estrutura subjacente de cada mensagem. Essa etapa de pré-processamento abstrai os dados das variáveis, gerando uma &quot;impressão digital&quot; simplificada que nos permite agrupar logs relacionados.</p><p>A lógica de mapeamento é simples para garantir velocidade e consistência:</p><ol><li>Abstração de dígitos: qualquer sequência de dígitos (0-9) é substituída por um único &quot;0&quot;.</li><li>Abstração de texto: qualquer sequência de caracteres alfabéticos com espaço em branco é substituída por um único &quot;a&quot;.</li><li>Normalização de espaço em branco: todas as sequências de espaço em branco (espaços, tabulações, novas linhas) são reduzidos a um único espaço.</li><li>Preservação de símbolos: pontuação e caracteres especiais (por exemplo, :, [, ], /) são preservados, pois normalmente são os indicadores mais fortes da estrutura log.</li></ol><p>Apresentamos a abordagem de mapeamento de log. Os padrões básicos de mapeamento incluem os seguintes:</p><ul><li>Dígitos de 0 a 9 de qualquer comprimento -&gt; até &quot;0&quot;.</li><li>Texto (caracteres alfabéticos com espaços) de qualquer comprimento -&gt; para &quot;a&quot;.</li><li>Espaços em branco, abas e novas linhas -&gt; para um único espaço.</li></ul><p>Vamos ver um exemplo de como esse mapeamento nos permite transformar os logs.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/78fa2887486eb9417804354ee3bf2a4fdb0f6383-846x252.png" style="width: 100%;" /><p>Como resultado, obtemos as seguintes máscaras de log:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec439a3d3a25002498b97defcff733ea5ebc6b55-826x94.png" style="width: 100%;" /><p>Observe as impressões digitais dos dois primeiros logs. Apesar dos diferentes carimbos de data e hora, classes de origem e conteúdo da mensagem, os prefixos (<code>0/0/0 0:0:0 a a.a:</code>) são idênticos. Esse alinhamento estrutural nos permite colocar automaticamente esses logs em buckets no mesmo cluster.</p><p>O terceiro log, no entanto, produz uma impressão digital completamente divergente (<code>0-0-0...</code>). Isso nos permite separá-lo algoritmicamente do primeiro grupo <em>antes</em> mesmo de invocarmos um LLM.</p><h2>Parte bônus: Implementação instantânea com ES|QL</h2><p>É tão simples quanto passar essa consulta no Discover.</p><pre><code>FROM loghub |
EVAL pattern = REPLACE(REPLACE(REPLACE(REPLACE(raw_message, "[ \t\n]+", " "), "[A-Za-z]+", "a"), "[0-9]+", "0"), "a( a)+", "a") |
STATS total_count = COUNT(), ratio = COUNT() / 2000.0, datasources=VALUES(filename), example=TOP(raw_message, 3, "desc") BY SUBSTRING(pattern, 0, 15) |
SORT total_count DESC |
LIMIT 100</code></pre><p><strong>Detalhamento da consulta:</strong></p><p><strong>DE</strong> loghub: direcionado para nosso índice contendo os dados de registro bruto.</p><p>Padrão <strong>EVAL</strong> = ...: a lógica de mapeamento do núcleo. Encadeamos funções REPLACE para realizar a abstração (por exemplo, dígitos para &#x27;0&#x27;, texto para &#x27;a&#x27;, etc.) e salvamos o resultado em um campo &quot;padrão&quot;.</p><p><strong>STATS </strong>[column1 =] expression1, …<strong> POR </strong>SUBSTRING(pattern, 0, 15):</p><p>Esta é uma etapa de clustering. Agrupamos logs que compartilham os primeiros 15 caracteres de seu padrão e criamos campos agregados, como contagem total de log por grupo, lista de fontes de dados de log, prefixo do padrão, 3 exemplos de log</p><p><strong>SORT</strong> total_count DESC | <strong>LIMITE</strong> 100: destaca os 100 padrões de log mais frequentes</p><p>Os resultados das consultas no LogHub estão exibidos abaixo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b119498f124376c41d242a099bf9081fd6536be8-1600x394.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4dcfc0a5b7fa753497cc5def5ea3cd54449c0481-1600x719.png" style="width: 100%;" /><p>Como demonstrado na visualização, essa abordagem &quot;livre de LLM&quot; particiona logs com alta precisão. Ela agrupou com sucesso 10 das 16 fontes de dados (com base nos rótulos do LogHub) (&gt;90%) e alcançou clustering majoritário em 13 das 16 fontes (&gt;60%), tudo isso sem necessidade de limpeza adicional, pré-processamento nem ajuste fino.</p><p>A impressão digital do formato de Log oferece uma alternativa pragmática e de alto impacto, além de ser um complemento para soluções sofisticadas de ML, como <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-categorize-text-aggregation">a análise de padrões de log</a>. Ele fornece insights imediatos sobre relacionamentos de logs e gerencia efetivamente grandes clusters de logs.</p><ul><li>Versatilidade como primitiva </li></ul><p>Graças à implementação do <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">ES|QL</a>, o LFF funciona tanto como uma ferramenta independente para diagnósticos/visualizações de dados rápidos, quanto como um componente essencial em pipelines de análise de logs para casos de uso de alto volume. </p><ul><li>Flexibilidade</li></ul><p>O LFF é fácil de personalizar e estender para captar padrões específicos, ou seja, números hexadecimais e endereços IP.</p><ul><li>Estabilidade determinística</li></ul><p>Ao contrário dos algoritmos de clustering baseados em ML, a lógica LFF é direta e determinística. Novos logs recebidos não afetam retroativamente os clusters de logs existentes.</p><ul><li>Desempenho e memória</li></ul><p>Requer memória mínima, sem treinamento nem GPU, tornando-o ideal para ambientes de alta taxa em tempo real.</p><h2>Combinando a impressão digital do formato de log com um LLM</h2><p>Para validar a arquitetura híbrida proposta, cada experimento continha um subconjunto aleatório de 20% dos registros de cada fonte de dados. Essa restrição simula um ambiente de produção real onde os logs são processados em lotes, em vez de um despejo histórico monolítico.</p><p>O objetivo era demonstrar que o LFF atua como uma camada de compressão eficaz. Nosso objetivo era provar que regras de análise de alta cobertura poderiam ser geradas a partir de amostras pequenas e selecionadas e generalizadas com sucesso para todo o conjunto de dados.</p><h2>Pipeline de execução</h2><p>Implementamos um pipeline de múltiplas etapas que filtra, agrupa e aplica amostragem estratificada aos dados antes que cheguem ao LLM.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b3f46ea471760b406a32fc7d4bc74cc03faaced2-3840x1660.png" style="width: 100%;" /><p>1. Clustering hierárquico em dois estágios</p><ul><li>Subclasses (correspondência exata): os logs são agregados por impressões digitais idênticas. Todo log em uma subclasse compartilha exatamente a mesma estrutura de formato.</li><li>Limpeza de discrepâncias. Nós descartamos quaisquer subclasses que representam menos de 5% do volume total de log. Isso garante que o LLM se concentre no sinal dominante e não seja desviado por ruído ou logs malformados.</li><li>Metaclasses (correspondência de prefixo): as subclasses restantes são agrupadas em metaclasses pelos primeiros N caracteres da correspondência da impressão digital do formato. Essa estratégia de agrupamento divide efetivamente formatos lexicalmente semelhantes sob uma mesma categoria. Escolhemos N=5 para análise de log e N=15 para particionamento de log quando as fontes de dados são desconhecidas.</li></ul><p>2. Amostragem estratificada. Após a construção da árvore hierárquica, construímos a amostra de log para o LLM. O objetivo estratégico é maximizar a cobertura de variações enquanto minimiza o uso de tokens.</p><ul><li>Selecionamos logs representativos de <em>cada</em> subclasse válida dentro da metaclasse mais ampla.</li><li>Para gerenciar um caso extremo de subclasses muito numerosas, aplicamos subamostragem aleatória para ajustar ao tamanho da janela alvo.</li></ul><p>3. Geração de regras Final, solicitamos ao LLM que gere uma regra de análise regex que se encaixe em todos os logs da amostra fornecida para cada metaclasse. Para nossa PoC, usamos o modelo mini GPT-4o.</p><h2>Resultados experimentais e observações</h2><p>Alcançamos 94% de precisão de análise sintática e 91% de precisão de particionamento no conjunto de dados do Loghub.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/49b2b6a1401dd1f33951da68e5a3fac37d0b5aaa-1600x1506.png" style="width: 100%;" /><p>A matriz de confusão acima ilustra os resultados da partição log. O eixo vertical representa as fontes de dados reais e o eixo horizontal representa as fontes de dados previstas. A intensidade do heatmap corresponde ao volume do log, com blocos mais claros indicando uma contagem maior. O alinhamento diagonal demonstra a alta fidelidade do modelo na atribuição da fonte, com espalhamento mínimo.</p><h2>Nossos insights sobre benchmarks de desempenho:</h2><ul><li><strong>Linha de base ideal:</strong> uma janela de contexto de <strong>30 a 40 amostras de log</strong> por categoria provou ser o ponto ideal, produzindo consistentemente uma análise robusta com padrões Regex e Grok.</li><li><strong>Minimização da entrada:</strong> aumentamos o tamanho da entrada para 10 registros por categoria para padrões Regex e observamos uma queda de apenas 2% no desempenho da análise, confirmando que a amostragem baseada na diversidade é mais importante do que o volume bruto.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Uma introdução aos modelos Jina, sua funcionalidade e seus usos no Elasticsearch]]></title>
            <description><![CDATA[Confira os embeddings multimodais Jina, o Reranker v3 e os modelos semânticos de embedding, além de como usá-los nativamente no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/jina-models-elasticsearch-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/jina-models-elasticsearch-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Scott Martens]]></dc:creator>
            <pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/407b4c862b51ebdfc7f26db4e25950a65caf1673-656x442.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Jina by Elastic fornece modelos de base para busca voltados a aplicações e automação de processos de negócio. Esses modelos oferecem funcionalidades essenciais para levar IA a aplicações no Elasticsearch e a projetos inovadores baseados em IA.</p><p>Os modelos Jina se enquadram em três grandes categorias, projetadas para dar suporte ao processamento, à organização e à recuperação de informações:</p><ul><li>Modelos de embedding semântico</li><li>Modelos de reclassificação</li><li>Modelos de linguagem generativos de pequeno porte</li></ul><h2>Modelos de embedding semântico</h2><p>A ideia por trás dos embeddings semânticos é que um modelo de IA pode aprender a representar aspectos do significado de suas entradas em termos da geometria de espaços de alta dimensionalidade.</p><p>É possível pensar em um embedding semântico como um ponto (tecnicamente, um <em>vetor</em>) em um espaço de alta dimensionalidade. Um modelo de embedding é uma rede neural que recebe algum tipo de dado digital como entrada, potencialmente qualquer tipo, mas mais comumente texto ou imagem, e produz a localização de um ponto correspondente em um espaço de alta dimensionalidade, representada por um conjunto de coordenadas numéricas. Quando o modelo executa bem sua função, a distância entre dois embeddings semânticos é proporcional ao quanto os objetos digitais correspondentes compartilham o mesmo significado.</p><p>Para entender por que isso é importante para aplicações de busca, imagine um embedding para a palavra “cão” e outro para a palavra “gato” como pontos em um espaço.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/802cf9bbcb82180d3fc91009f9f62027eee8f031-615x615.png" style="width: 100%;" /><p>Um bom modelo de embedding deve gerar um embedding para a palavra “felino” muito mais próximo de “gato” do que de “cão”, e “canino” deve ter um embedding muito mais próximo de “cão” do que de “gato”, porque essas palavras têm praticamente o mesmo significado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bce49daf9a31b8fb7ce1c6ef7ae4e8117a4e8b33-615x615.png" style="width: 100%;" /><p>Se um modelo for multilíngue, espera-se o mesmo comportamento para traduções de “gato” e “cão” em outros idiomas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ce4d030385324526cbd7539140e0e634d939371c-615x615.png" style="width: 100%;" /><p>Modelos de embedding traduzem similaridade ou dissimilaridade de significado entre elementos em relações espaciais entre embeddings. As imagens acima têm apenas duas dimensões para que seja possível visualizá-las na tela, mas modelos de embedding produzem vetores com dezenas a milhares de dimensões. Isso permite codificar sutilezas de significado para textos inteiros, atribuindo um ponto em um espaço com centenas ou milhares de dimensões a documentos com milhares de palavras ou mais.</p><h2>Embeddings multimodais</h2><p>Modelos multimodais estendem o conceito de embeddings semânticos para além de textos, especialmente para imagens. Espera-se que o embedding de uma imagem fique próximo ao embedding de uma descrição fiel dessa imagem.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ac6aef5b1423e5fe4853e8a547a74e66b0885c2-615x615.png" style="width: 100%;" /><p>Embeddings semânticos têm muitos usos. Entre outras aplicações, é possível usá-los para criar classificadores eficientes, realizar clustering de dados e executar diversas tarefas, como deduplicação de dados e investigação da diversidade dos dados, ambas importantes para aplicações de big data que lidam com volumes de informação grandes demais para serem gerenciados manualmente.</p><p>O principal uso direto de embeddings está na recuperação de informações. O Elasticsearch pode armazenar objetos de recuperação com embeddings como chaves. As consultas são convertidas em vetores de embedding, e a busca retorna os objetos armazenados cujas chaves estão mais próximas do embedding da consulta.</p><p>Enquanto <em>a recuperação tradicional baseada em vetores</em> (às vezes chamada de <em>recuperação por vetores esparsos</em>) usa vetores baseados em palavras ou metadados presentes em documentos e consultas, a <em>recuperação baseada em embeddings</em> (também conhecida como <em>recuperação por vetores densos</em>) usa significados avaliados por IA em vez de palavras. Isso a torna, em geral, muito mais flexível e mais precisa do que métodos tradicionais de busca.</p><h2>Aprendizado de representação Matryoshka</h2><p>O número de dimensões de um embedding, assim como a precisão dos valores numéricos que o compõem, tem impactos significativos na performance. Espaços de dimensionalidade muito alta e números de precisão extremamente elevada podem representar informações altamente detalhadas e complexas, mas exigem modelos de IA maiores, mais caros para treinar e para executar. Os vetores que esses modelos geram requerem mais espaço de armazenamento, e são necessários mais ciclos de computação para calcular as distâncias entre eles. Usar modelos de embedding semântico envolve fazer concessões importantes entre precisão e consumo de recursos.</p><p>Para maximizar a flexibilidade para os usuários, os modelos Jina são treinados com uma técnica chamada <a href="https://arxiv.org/abs/2205.13147">Aprendizado de Representação Matryoshka</a>. Essa abordagem faz com que os modelos concentrem as distinções semânticas mais importantes nas primeiras dimensões do vetor de embedding, de modo que seja possível descartar as dimensões mais altas e ainda assim obter bom desempenho.</p><p>Na prática, isso significa que usuários dos modelos Jina podem escolher quantas dimensões desejam que seus embeddings tenham. Escolher menos dimensões reduz a precisão, mas a degradação de performance é pequena. Na maioria das tarefas, as métricas de performance dos modelos Jina caem entre 1% e 2% sempre que o tamanho do embedding é reduzido em 50%, até uma redução total de cerca de 95% no tamanho.</p><h2>Recuperação assimétrica</h2><p>A similaridade semântica geralmente é medida de forma simétrica. O valor obtido ao comparar “gato” com “cão” é o mesmo que ao comparar “cão” com “gato”. No entanto, quando embeddings são usados para recuperação de informações, o desempenho melhora quando essa simetria é quebrada e as consultas são codificadas de forma diferente dos objetos de recuperação.</p><p>Isso ocorre por causa da forma como treinamos modelos de embedding. Os dados de treinamento contêm ocorrências dos mesmos elementos, como palavras, em muitos contextos diferentes, e os modelos aprendem semântica comparando similaridades e diferenças contextuais entre esses elementos.</p><p>Assim, por exemplo, pode acontecer de a palavra “animal” não aparecer em muitos dos mesmos contextos que “gato” ou “cão”, e, portanto, o embedding de “animal” não ficar particularmente próximo de “gato” ou “cão”.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9a33163405af6c71ee7f4ba8ebc86af39e295a69-615x615.png" style="width: 100%;" /><p>Isso torna menos provável que uma consulta por “animal” recupere documentos sobre gatos e cães — justamente o oposto do nosso objetivo. Por isso, em vez disso, codificamos “animal” de forma diferente quando ele aparece como consulta do que quando é um alvo de recuperação.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/363992d4f0affba7937c0c8a9f82c9a531fcd3ba-615x615.png" style="width: 100%;" /><p><em>Recuperação assimétrica</em> significa usar um modelo diferente para consultas ou treinar especificamente um modelo de embedding para codificar os dados de uma forma quando são armazenados para recuperação e de outra forma quando são usados como consultas.</p><h2>Embeddings multivetoriais</h2><p>Embeddings únicos funcionam bem para recuperação de informações porque se encaixam no modelo básico de um banco de dados indexado: armazenamos objetos para recuperação usando um único vetor de embedding como chave de recuperação. Quando usuários consultam o repositório de documentos, suas consultas são traduzidas em vetores de embedding, e os documentos cujas chaves estão mais próximas do embedding da consulta, no espaço de embeddings de alta dimensionalidade, são recuperados como candidatos.</p><p>Embeddings multivetoriais funcionam de forma um pouco diferente. Em vez de gerar um vetor de comprimento fixo para representar uma consulta e um objeto armazenado inteiro, eles produzem uma sequência de embeddings que representam partes menores desses elementos. Essas partes geralmente são tokens ou palavras no caso de textos, e blocos de imagem no caso de dados visuais. Esses embeddings refletem o significado de cada parte dentro de seu contexto.</p><p>Por exemplo, considere estas frases:</p><ul><li>Ela tinha um coração de ouro.</li><li>Ela fez das tripas coração.</li><li>Ela teve um ataque do coração.</li></ul><p>Superficialmente, essas frases parecem muito semelhantes, mas um modelo multivetorial provavelmente geraria embeddings bem diferentes para cada ocorrência de “coração”, representando como cada uma assume um significado distinto no contexto da frase como um todo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a33e60c8d8ee3d312bca8375ca2a8b0a0cd40ba9-615x615.png" style="width: 100%;" /><p>Comparar dois objetos por meio de seus embeddings multivetoriais geralmente envolve medir a distância de Chamfer: comparar cada parte de um embedding multivetorial com cada parte de outro e somar as menores distâncias entre elas. Outros sistemas, incluindo os reclassificadores Jina descritos abaixo, usam esses embeddings como entrada para um modelo de IA treinado especificamente para avaliar sua similaridade. Ambas as abordagens normalmente apresentam maior precisão do que a simples comparação de embeddings de vetor único, porque embeddings multivetoriais contêm informações muito mais detalhadas do que embeddings de vetor único.</p><p>No entanto, embeddings multivetoriais não são adequados para indexação. Eles costumam ser usados em tarefas de reclassificação, conforme descrito para o modelo <code>jina-colbert-v2</code> na próxima seção.</p><h2>Modelos de embedding Jina</h2><h3>Jina embeddings v4</h3><p><a href="https://jina.ai/news/jina-embeddings-v4-universal-embeddings-for-multimodal-multilingual-retrieval/"><strong>jina-embeddings-v4</strong></a> é um modelo de embedding multilíngue e multimodal, com 3,8 bilhões (3,8 × 10⁹) de parâmetros, que oferece suporte a imagens e textos em diversos idiomas amplamente utilizados. Ele utiliza uma arquitetura inédita para aproveitar conhecimento visual e conhecimento linguístico, melhorando o desempenho em ambas as tarefas e permitindo que o modelo se destaque na recuperação de imagens e, especialmente, na <a href="https://huggingface.co/tasks/visual-document-retrieval">recuperação de documentos visuais</a>. Isso significa que ele lida bem com imagens como gráficos, slides, mapas, capturas de tela, digitalizações de páginas e diagramas — tipos comuns de imagens que muitas vezes contêm texto incorporado importante e que ficam fora do escopo de modelos de visão computacional treinados apenas com imagens de cenas do mundo real.</p><p>Otimizamos esse modelo para diversas tarefas diferentes usando <a href="https://huggingface.co/docs/peft/en/package_reference/lora">adaptadores compactos de Low-Rank Adaptation (LoRA)</a>. Isso nos permite treinar um único modelo para se especializar em múltiplas tarefas, sem comprometer o desempenho em nenhuma delas, com um custo adicional mínimo de memória ou processamento.</p><p>Os principais recursos incluem:</p><ul><li>Desempenho de ponta na recuperação de documentos visuais, além de suporte a texto multilíngue e imagens comuns com resultados que superam significativamente modelos muito maiores.</li><li>Suporte a grandes tamanhos de contexto de entrada: 32.768 tokens equivalem aproximadamente a 80 páginas de texto em inglês com espaçamento duplo, e 20 megapixels equivalem a uma imagem de 4.500 × 4.500 pixels.</li><li>Tamanhos de embedding selecionáveis pelo usuário, de um máximo de 2.048 dimensões até 128 dimensões. Constatamos empiricamente que o desempenho se degrada de forma acentuada abaixo desse limite.</li><li>Suporte tanto a embeddings únicos quanto a embeddings multivetoriais. Para textos, a saída multivetorial consiste em um embedding de 128 dimensões para cada token de entrada. Para imagens, é gerado um embedding de 128 dimensões para cada bloco de 28 × 28 pixels necessário para cobrir a imagem.</li><li>Otimização para recuperação assimétrica por meio de um par de adaptadores LoRA treinados especificamente para esse propósito.</li><li>Um adaptador LoRA otimizado para cálculo de similaridade semântica.</li><li>Suporte especial a linguagens de programação e estruturas de TI, também por meio de um adaptador LoRA.</li></ul><p>Desenvolvemos <code>jina-embeddings-v4</code> para atuar como uma ferramenta geral e multifuncional para uma ampla gama de tarefas comuns de busca, compreensão de linguagem natural e análise com IA. Apesar de ser relativamente pequeno considerando suas capacidades, ainda exige recursos significativos para implantação e é mais adequado para uso por meio de uma API em nuvem ou em ambientes de alto volume.</p><h3>Jina embeddings v3</h3><p><a href="https://jina.ai/news/jina-embeddings-v3-a-frontier-multilingual-embedding-model/"><strong>jina-embeddings-V3</strong></a> é um modelo de embedding compacto, multilíngue, somente para texto, com alto desempenho e menos de 600 milhões de parâmetros. Ele oferece suporte a até 8.192 tokens de texto de entrada e gera embeddings de vetor único com tamanhos escolhidos pelo usuário, desde o padrão de 1.024 dimensões até 64.</p><p>Treinamos <code>jina-embeddings-v3</code> para uma variedade de tarefas de texto — não apenas recuperação de informações e similaridade semântica, mas também tarefas de classificação, como análise de sentimento e moderação de conteúdo, além de tarefas de clusterização, como agregação de notícias e recomendação. Assim como <code>jina-embeddings-v4</code>, esse modelo oferece adaptadores LoRA especializados para as seguintes categorias de uso:</p><ul><li>Recuperação assimétrica</li><li>Similaridade semântica</li><li>Classificação</li><li>Clustering</li></ul><p><code>jina-embeddings-v3</code> é um modelo muito menor do que <code>jina-embeddings-v4</code> com um tamanho de contexto de entrada significativamente reduzido, mas com custo operacional mais baixo. Ainda assim, apresenta desempenho bastante competitivo, embora apenas para textos, e é uma escolha melhor para muitos casos de uso.</p><h3>Incorporações de código Jina</h3><p>Os modelos especializados de embedding de código da Jina — <a href="https://jina.ai/models/jina-code-embeddings-1.5b"><strong>jina-code-embeddings (0.5b e 1.5b)</strong></a> — oferecem suporte a 15 esquemas de programação e estruturas, além de textos em inglês relacionados a computação e tecnologia da informação. São modelos compactos, com meio bilhão (0,5 × 10⁹) e um bilhão e meio (1,5 × 10⁹) de parâmetros, respectivamente. Ambos oferecem suporte a tamanhos de contexto de entrada de até 32.768 tokens e permitem que os usuários escolham os tamanhos dos embeddings de saída, de 896 a 64 dimensões no modelo menor e de 1.536 a 128 no modelo maior.</p><p>Esses modelos oferecem suporte a recuperação assimétrica para cinco especializações específicas de tarefa, usando <a href="https://arxiv.org/abs/2101.00190">ajuste de prefixo</a> em vez de adaptadores LoRA:</p><ul><li><strong>Código para código.</strong> Recuperar código semelhante entre diferentes linguagens de programação. Isso é usado para alinhamento de código, deduplicação de código e suporte a portabilidade e refatoração.</li><li><strong>Linguagem natural para código.</strong> Recuperar código que corresponda a consultas em linguagem natural, comentários, descrições e documentação.</li><li><strong>Código para linguagem natural. </strong>Associar código a documentação ou a outros textos em linguagem natural.</li><li><strong>Conclusão de código para código.</strong> Sugerir código relevante para completar ou aprimorar código existente.</li><li><strong>Perguntas e respostas técnicas.</strong> Identificar respostas em linguagem natural para perguntas sobre tecnologias da informação, sendo ideal para casos de uso de suporte técnico.</li></ul><p>Esses modelos oferecem performance superior em tarefas que envolvem documentação técnica e materiais de programação, com um custo computacional relativamente baixo. Eles são bem adequados para integração em ambientes de desenvolvimento e assistentes de código.</p><h3>Jina ColBERT v2</h3><p><a href="https://jina.ai/models/jina-colbert-v2"><strong>jina-colbert-v2</strong></a> é um modelo de embedding de texto multivetorial com 560 milhões de parâmetros. Ele é multilíngue, treinado com materiais em 89 idiomas, e oferece suporte a tamanhos variáveis de embedding e recuperação assimétrica.</p><p>Como observado anteriormente, embeddings multivetoriais não são adequados para indexação, mas são muito úteis para aumentar a precisão dos resultados de outras estratégias de busca. Com <code>jina-colbert-v2</code><strong>,</strong> é possível calcular embeddings multivetoriais antecipadamente e usá-los para reclassificar candidatos à recuperação no momento da consulta. Essa abordagem é menos precisa do que usar um dos modelos de reclassificação descritos na próxima seção, mas é muito mais eficiente, pois envolve apenas a comparação de embeddings multivetoriais armazenados, em vez de invocar todo o modelo de IA para cada consulta e cada correspondência candidata. Ela é especialmente adequada para casos de uso em que a latência e a sobrecarga computacional dos modelos de reclassificação são excessivas ou em que o número de candidatos a comparar é grande demais para esse tipo de modelo.</p><p>Esse modelo gera uma sequência de embeddings, um por token de entrada, e os usuários podem selecionar embeddings de tokens com 128, 96 ou 64 dimensões. As correspondências de texto candidatas são limitadas a 8.192 tokens. As consultas são codificadas de forma assimétrica, portanto é necessário especificar se um texto é uma consulta ou uma correspondência candidata, além de limitar consultas a 32 tokens.</p><h3>Jina CLIP v2</h3><p><a href="https://jina.ai/news/jina-clip-v2-multilingual-multimodal-embeddings-for-text-and-images/"><strong>jina-clip-v2</strong></a> é um modelo de embedding multimodal com 900 milhões de parâmetros, treinado para que textos e imagens gerem embeddings próximos entre si quando o texto descreve o conteúdo da imagem. Seu uso principal é a recuperação de imagens com base em consultas textuais, mas ele também é um modelo somente de texto com alto desempenho, reduzindo custos para os usuários, já que não é necessário manter modelos separados para recuperação de texto para texto e de texto para imagem.</p><p>Esse modelo oferece suporte a um contexto de entrada de texto de 8.192 tokens, e as imagens são redimensionadas para 512 × 512 pixels antes da geração dos embeddings.</p><p>Arquiteturas de pré-treinamento contrastivo de linguagem e imagem (CLIP) são fáceis de treinar e operar e podem gerar modelos muito compactos, mas apresentam algumas limitações fundamentais. Eles não conseguem usar conhecimento de um meio para melhorar seu desempenho em outro. Ou seja, não conseguem aproveitar informações de um meio para aprimorar o desempenho em outro. Assim, embora um modelo possa saber que as palavras “cão” e “gato” são mais próximas em significado entre si do que qualquer uma delas em relação a “carro”, ele não necessariamente saberá que a imagem de um cão e a imagem de um gato são mais relacionadas entre si do que qualquer uma delas em relação à imagem de um carro.</p><p>Esses modelos também sofrem do que se chama de <em>lacuna de modalidade</em>: um embedding de um texto sobre cães tende a ficar mais próximo de um embedding de um texto sobre gatos do que de um embedding de uma imagem de cães. Por causa dessa limitação, recomendamos usar CLIP como um modelo de recuperação de texto para imagem ou como um modelo somente de texto, mas não misturar os dois em uma única consulta.</p><h2>Modelos de reclassificação</h2><p>Modelos de reclassificação recebem como entrada uma consulta e uma ou mais correspondências candidatas e as comparam diretamente, produzindo correspondências com precisão muito maior.</p><p>Em princípio, seria possível usar um reclassificador diretamente para recuperação de informações, comparando cada consulta com cada documento armazenado, mas isso seria computacionalmente muito caro e impraticável para qualquer coleção que não seja muito pequena. Por isso, reclassificadores tendem a ser usados para avaliar listas relativamente curtas de correspondências candidatas encontradas por outros meios, como busca baseada em embeddings ou outros algoritmos de recuperação. Modelos de reclassificação são ideais para esquemas de busca híbrida e federada, nos quais executar uma busca pode significar enviar consultas a sistemas de busca separados, com conjuntos de dados distintos, cada um retornando resultados diferentes. Eles funcionam muito bem para combinar resultados diversos em um único resultado de alta qualidade.</p><p>A busca baseada em embeddings pode exigir um grande investimento, envolvendo a reindexação de todos os dados armazenados e a mudança das expectativas dos usuários em relação aos resultados. Adicionar um reclassificador a um esquema de busca existente pode trazer muitos dos benefícios da IA sem a necessidade de reestruturar toda a solução de busca.</p><h2>Modelos de reclassificação Jina</h2><h3>Jina Reranker m0</h3><p><a href="https://jina.ai/models/jina-reranker-m0/"><strong>jina-reranker-m0</strong></a> é um reclassificador multimodal com 2,4 bilhões (2,4 × 10⁹) de parâmetros, que oferece suporte a consultas textuais e a correspondências candidatas compostas por textos e/ou imagens. Ele é o principal modelo para recuperação de documentos visuais, o que o torna uma solução ideal para repositórios de PDFs, digitalizações de texto, capturas de tela e outras imagens geradas ou modificadas por computador que contêm texto ou outras informações semiestruturadas, bem como para dados mistos compostos por documentos de texto e imagens.</p><p>Esse modelo recebe uma única consulta e uma correspondência candidata e retorna uma pontuação. Quando a mesma consulta é usada com diferentes candidatos, as pontuações são comparáveis e podem ser usadas para ranqueá-los. Ele oferece suporte a um tamanho total de entrada de até 10.240 tokens, incluindo o texto da consulta e o texto ou imagem candidata. Cada bloco de 28 × 28 pixels necessário para cobrir uma imagem conta como um token no cálculo do tamanho de entrada.</p><h3>Jina Reranker v3</h3><p><a href="https://jina.ai/models/jina-reranker-v3/"><strong>jina-reranker-v3</strong></a> é um reclassificador de texto com 600 milhões de parâmetros, com desempenho de ponta entre modelos de tamanho comparável. Ao contrário de <code>jina-reranker-m0</code>, ele recebe uma única consulta e uma lista de até 64 correspondências candidatas e retorna a ordem de ranqueamento. Ele tem um contexto de entrada de 131.000 tokens, incluindo a consulta e todos os candidatos de texto.</p><h3>Jina Reranker v2</h3><p><a href="https://jina.ai/models/jina-reranker-v2"><strong>jina-reranker-v2-base-multilingual</strong></a> é um reclassificador multifuncional, de uso geral, muito compacto, com recursos adicionais projetados para oferecer suporte a chamadas de função e consultas SQL. Com menos de 300 milhões de parâmetros, ele fornece reclassificação de texto multilíngue rápida, eficiente e precisa, com suporte adicional para selecionar tabelas SQL e funções externas que correspondam a consultas de texto, o que o torna adequado para casos de uso com IA agêntica.</p><h2>Modelos de linguagem generativos de pequeno porte</h2><p>Modelos de linguagem generativos são modelos como o ChatGPT da OpenAI, o Google Gemini e o Claude, da Anthropic, que recebem entradas em texto ou multimídia e respondem com saídas em texto. Não existe um limite bem definido que separe modelos de linguagem <em>grandes</em> (LLMs) de modelos de linguagem <em>pequenos</em> (SLMs), mas os desafios práticos de desenvolver, operar e usar LLMs de ponta são bem conhecidos. Os modelos mais conhecidos não são distribuídos publicamente, portanto só é possível estimar seu tamanho, mas espera-se que ChatGPT, Gemini e Claude estejam na faixa de 1 a 3 trilhões (1–3 × 10¹²) de parâmetros.</p><p>Executar esses modelos, mesmo quando estão disponíveis publicamente, está muito além do alcance de hardware convencional, exigindo os chips mais avançados organizados em grandes arranjos paralelos. É possível acessar LLMs por meio de APIs pagas, mas isso envolve custos significativos, alta latência e dificuldades para atender a exigências de proteção de dados, soberania digital e repatriação de nuvem. Além disso, os custos relacionados ao treinamento e à personalização de modelos desse porte podem ser consideráveis.</p><p>Consequentemente, uma grande quantidade de pesquisa tem se concentrado no desenvolvimento de modelos menores que, embora não tenham todas as capacidades dos maiores LLMs, conseguem executar tipos específicos de tarefas com a mesma qualidade, a um custo reduzido. Empresas normalmente implantam software para resolver problemas específicos, e com software de IA não é diferente; por isso, soluções baseadas em SLMs costumam ser preferíveis às baseadas em LLMs. Elas geralmente podem ser executadas em hardware comum, são mais rápidas, consomem menos energia e são muito mais fáceis de personalizar.</p><p>As ofertas de SLM da Jina estão crescendo à medida que nos concentramos em como levar IA da melhor forma possível a soluções práticas de busca.</p><h2>Jina SLMs</h2><h3>ReaderLM v2</h3><p><a href="https://jina.ai/models/ReaderLM-v2"><strong>ReaderLM-v2</strong></a> é um modelo de linguagem generativo que converte HTML em Markdown ou em JSON, de acordo com esquemas JSON fornecidos pelo usuário e instruções em linguagem natural.</p><p>O pré-processamento e a normalização de dados são uma parte essencial do desenvolvimento de boas soluções de busca para dados digitais, mas dados do mundo real, especialmente informações derivadas da web, costumam ser caóticos, e estratégias simples de conversão frequentemente se mostram frágeis. Em vez disso, <code>ReaderLM-v2</code> oferece uma solução inteligente baseada em modelo de IA, capaz de entender o caos de um dump de árvore DOM de uma página da web e identificar, de forma robusta, elementos úteis.</p><p>Com 1,5 bilhão (1,5 × 10⁹) de parâmetros, esse modelo é três ordens de magnitude mais compacto do que LLMs de última geração, mas apresenta desempenho equivalente a eles nessa tarefa específica e bastante restrita.</p><h3>Jina VLM</h3><p><a href="https://jina.ai/models/jina-vlm"><strong>jina-VLC</strong></a> é um modelo de linguagem generativo com 2,4 bilhões (2,4 × 10⁹) de parâmetros, treinado para responder a perguntas em linguagem natural sobre imagens. Ele oferece suporte muito robusto a análise de documentos visuais, isto é, responder a perguntas sobre digitalizações, capturas de tela, slides, diagramas e dados de imagem semelhantes que não são naturais.</p><p>Por exemplo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/adfa1420d079ca4fd5582eef4349b1265b378e76-950x500.png" style="width: 100%;" /><p>Ele também é muito eficiente na leitura de texto em imagens:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ea3956e7ad86f8e171841cab2c28c8b3498da1d4-1002x500.png" style="width: 100%;" /><p>Mas é na compreensão do conteúdo de imagens informativas e produzidas pelo ser humano que <code>jina-vlm</code> realmente se destaca:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f68606f9d2d99e2cd616d4ff81db3574dc4e26a5-1020x700.png" style="width: 100%;" /><p>Ou:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/297e85e7e78f296388a02301e1e08fed70827423-1000x500.png" style="width: 100%;" /><p><code>jina-vlm</code> é especialmente adequado para geração automática de legendas, descrições de produtos, texto alternativo de imagens e aplicações de acessibilidade para pessoas com deficiência visual. Além disso, cria novas possibilidades para sistemas de geração aumentada por recuperação (RAG) utilizarem informações visuais e para agentes de IA processarem imagens sem assistência humana.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introdução ao Elastic Agent Builder e Strands Agents SDK]]></title>
            <description><![CDATA[Aprenda a criar um agente com o Elastic Agent Builder e explore como usar o agente via protocolo A2A orquestrado com o Strands Agents SDK.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agent-builder-a2a-strands-agents-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agent-builder-a2a-strands-agents-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
            <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Você tem uma ideia para um agente de IA? Provavelmente isso envolve fazer algo com os dados, porque se um agente for iniciar uma ação útil, ele precisa tomar uma decisão e precisa dos dados certos para tomar a decisão certa.</p><p>O Elastic Agent Builder facilita a criação de agentes de IA conectados a dados. Mostraremos como fazer isso neste post do blog. Vamos passar por todos os passos necessários para criar um agente com uma ferramenta MCP que acesse os dados armazenados no Elastic. Depois, usaremos o Strands Agents SDK e os recursos Agent2Agent (A2A) para operar o agente. O <a href="https://strandsagents.com/">Strands Agents SDK</a> é uma plataforma de desenvolvimento de IA multiagente que você pode usar para criar apps agentes com código suficiente para garantir o resultado desejado.</p><p>Vamos construir um agente de IA que jogue RPS+, uma versão do clássico jogo Pedra, Papel e Tesoura com um diferencial: oferece aos jogadores algumas opções extras.</p><h2>Pré-requisitos</h2><p>Aqui está o que é necessário para seguir as etapas deste post do blog:</p><ul><li>Um editor de texto rodando no seu computador local<ul><li><a href="https://code.visualstudio.com/download">Visual Studio Code</a> é o que usaremos para as instruções de exemplo neste post do blog</li></ul></li><li><a href="https://www.python.org/downloads/">Python 3.10 ou superior</a> rodando no seu computador local</li></ul><h2>Crie um projeto serverless</h2><p>A primeira coisa de que precisamos é de um projeto Elasticsearch Serverless, que inclua o Elastic Agent Builder.</p><p>Acesse <a href="http://cloud.elastic.co/">cloud.elastic.co</a> e crie um novo projeto Elasticsearch Serverless.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" style="width: 100%;" /><h2>Crie um índice e adicione dados</h2><p>Em seguida, adicionaremos alguns dados ao nosso projeto Elasticsearch. Abra as Ferramentas de desenvolvedor, onde podemos executar comandos para criar um novo índice e inserir alguns dados. Selecione Ferramentas de desenvolvedor no menu de navegação de nível superior.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" style="width: 100%;" /><p>Copie e cole o seguinte comando PUT na área de entrada de solicitações do console Ferramentas de desenvolvedor. Essa declaração cria um índice Elasticsearch chamado &quot;game-docs&quot;.</p><pre><code>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}</code></pre><p>Clique no botão <strong>Enviar solicitação</strong> que aparece no lado direito da declaração em Ferramentas de desenvolvedor. Você deve ver uma notificação confirmando que o índice <em>game-docs</em> foi criado na área de resposta das Ferramentas de desenvolvedor.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" style="width: 100%;" /><p>Um índice chamado <em>game-docs</em> é um ótimo lugar para armazenar os dados do jogo que estamos criando. Vamos colocar um documento chamado <em>rps+-md</em> nesse índice que contém todos os dados que nosso jogo requer. Copie e cole o seguinte comando PUT no console Ferramentas de desenvolvedor.</p><pre><code>PUT /game-docs/_doc/rps+-md
{
  "title": "Rock Paper Scissors +",
  "content": "
# Game Name
RPS+

# Starting Prompt
Let's play RPS+ !
---
What do you choose?

# Game Objects
1. Rock 🪨 👊
2. Paper 📜 🖐
3. Scissors ✄ ✌️
4. Light ☼ 👍
5. Dark Energy ☄ 🫱

# Judgement of Victory
* Rock beats Scissors
  * because rocks break scissors
* Paper beats Rock
  * because paper covers rock
* Scissors beat Paper
  * because scissors cut paper
* Rock beats Light
  * because you can build a rock structure to block out light
* Paper beats Light
  * because knowledge stored in files and paper books helps us understand light
* Light beats Dark Energy
  * because light enables humans to lighten up and laugh in the face of dark energy as it causes the eventual heat death of the universe
* Light beats Scissors
  * because light is needed to use scissors safely
* Dark Energy beats Rock
  * because dark energy rocks more than rocks. It rocks rocks and everything else in its expansion of the universe
* Dark Energy beats Paper
  * because humans, with their knowledge stored in files and paper books, can't explain dark energy 
* Scissors beat Dark Energy
  * because a human running with scissors is darker than dark energy

# Invalid Input
I was hoping for an worthy opponent
  - but alas it appears that time has past
  - but alas there's little time for your todo list when [todo:fix this] is so vast

# Cancel Game
The future belongs to the bold. Goodbye..
",
  "filename": "RPS+.md",
  "last_modified": "2025-11-25T12:00:00Z"
}</code></pre><p>Clique no botão <strong>Enviar solicitação</strong> ao lado da declaração para executá-la e adicionar o documento <em>rps+-md</em> ao índice game-docs.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" style="width: 100%;" /><p>Agora devemos ter alguns dados para consultar e, com o Agent Builder, isso está mais simples do que nunca.</p><p>Selecione <strong>Agentes</strong> no menu de navegação principal.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" style="width: 100%;" /><p>Agora, é preciso perguntar ao Elastic AI Agent padrão: &quot;Quais dados eu tenho?&quot;</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" style="width: 100%;" /><p>O Elastic AI Agent avalia os dados e retorna uma explicação concisa sobre os dados que possuímos.</p><h2>Crie uma ferramenta</h2><p>Ok, agora temos alguns dados no Elastic, vamos utilizá-los. O Agent Builder inclui suporte integrado para criar ferramentas <a href="https://modelcontextprotocol.io/">MCP</a> que ajudam os agentes a acessar os dados necessários para ter o contexto correto para a tarefa. Vamos criar uma ferramenta simples que recupere os dados do nosso jogo.</p><p>Clique no menu de ações do Agent Builder.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" style="width: 100%;" /><p>Selecione <strong>Ver todas as ferramentas </strong>nas opções do menu.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" style="width: 100%;" /><p>Clique <strong>+ Nova Ferramenta.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" style="width: 100%;" /><p>No formulário <strong>Criar Ferramenta</strong>, selecione <strong><a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL.</a> </strong>Selecione a ferramenta <strong>Tipo</strong> e insira os valores a seguir.</p><p>Para o <strong>ID da Ferramenta</strong>:</p><pre><code>example.get_game_docs</code></pre><p>Para <strong>Descrição</strong>:</p><pre><code>Get RPS+ doc from Elasticsearch game-docs index.</code></pre><p>Para <strong>Configuração, </strong>insira a seguinte consulta na área de texto <strong>Mecanismo de consulta ES|QL: </strong></p><pre><code>FROM game-docs | WHERE filename == "RPS+.md"</code></pre><p>O formulário <strong>Criar ferramenta</strong> que você preencheu deve ter esta aparência: Clique em <strong>Salvar</strong> para criar a ferramenta.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" style="width: 100%;" /><p>Temos uma ferramenta nova no suporte de ferramentas. As ferramentas não devem ficar num suporte; elas devem ser usadas. Vamos criar um agente que possa usar nossa nova ferramenta personalizada.</p><h2>Crie um agente e atribua uma ferramenta a ele.</h2><p>Criar um agente é muito simples com o Agent Builder. Você só precisa digitar as instruções do agente com alguns detalhes. Vamos criar um agente agora.</p><p>Clique no botão <strong>Gerenciar agentes.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" style="width: 100%;" /><p>Clique<strong> + Novo agente.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" style="width: 100%;" /><p>Insira as informações a seguir no formulário <strong>Novo Agente</strong>.</p><p>Para o <strong>ID do Agente, </strong>insira o texto abaixo:</p><pre><code>rps_plus_agent</code></pre><p>Na área de texto de <strong>Instruções personalizadas, </strong>insira as seguintes instruções:</p><pre><code>When prompted, if the prompt contains an integer, then select the corresponding numbered item in the list of "Game Objects" from your documents. Otherwise select a random game object. This is your chosen game object for a single round of the game.

# General Game Rules
* 2 players
    - the user: the person playing the game
    - you: the agent playing the game and serving as the game master
* Each player chooses a game object which will be compared and cause them to tie, win or lose.

# Start the game
1. This is the way each new game always starts. You make the first line of your response only the name of your chosen game object. 

2. The remainder of your response should be the "Starting Prompt" text from your documents and generate a list of "Game Objects" for the person playing the game to choose a game object from.  

# End of Game: The game ends in one of the following three outcomes:
1. Invalid Input: If the player responds with an invalid game object choice, respond with variations of the "Invalid Input" text from your documents and then end the game.

2. Tie: The game ends in a tie if the user chooses the same game object as your game object choice.

3. Win or Lose: The game winner is decided based on the "Judgement of Victory" conditions from your documents. Compare the user's game object choice and your game object choice and determine who chose the winning game object.

# Game conclusion
Respond with a declaration of the winner of the game by outputting the corresponding text in the "Judgement of Victory" section of your documents.</code></pre><p>Para o <strong>Nome de exibição, </strong>insira o texto abaixo:</p><pre><code>RPS+ Agent</code></pre><p>Para a <strong>Descrição de exibição, </strong>insira o texto abaixo:</p><pre><code>An agent that plays the game RPS+</code></pre><p>Dê ao agente a ferramenta personalizada que criamos anteriormente, clicando na guia <strong>Ferramentas</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" style="width: 100%;" /><p>Selecione somente a ferramenta <em>example.get_game_docs</em> que criamos anteriormente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" style="width: 100%;" /><p>Clique em <strong>Salvar</strong> para criar o novo agente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" style="width: 100%;" /><p>Vamos testar nosso novo agente. Há um link prático para iniciar um bate-papo com qualquer agente da lista de agentes.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" style="width: 100%;" /><p>Basta digitar “iniciar jogo” e o jogo começará. Funciona!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" style="width: 100%;" /><p>O agente exibe a escolha de objeto de jogo na parte superior da resposta. Isso é útil porque podemos ver a escolha do agente e confirmar que o jogo está funcionando conforme o esperado. No entanto, saber a escolha do oponente antes de escolher não torna o jogo de Pedra, Papel e Tesoura muito divertido. Para aperfeiçoar e aprimorar o jogo até a forma final, podemos usar uma plataforma de orquestração de agentes que pode controlar agentes com código.</p><p>Agora é a hora do Strands Agents SDK.</p><h2>Strands Agents SDK</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" style="width: 100%;" /><p>Se você tem curiosidade em experimentar novas estruturas de desenvolvimento de agentes, então vale a pena dar uma chance ao <a href="https://strandsagents.com/latest/">Strands Agents SDK</a>. O <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">Strands Agents SDK foi lançado pela AWS (maio de 2025)</a> como uma implementação open source <a href="https://github.com/strands-agents/sdk-python">em Python</a>, e agora também existe uma versão <a href="https://dev.to/aws/strands-agents-now-speaks-typescript-a-side-by-side-guide-12b3">em Typescript</a>.</p><h2>Começando com o Strands Agents SDK em Python</h2><p>Preparem seus motores de programação, pois agora vamos percorrer o processo de clonagem e execução de um aplicativo de exemplo que usa Strands Agents para controlar o <em>agente RPS+</em> por meio do protocolo A2A. Vamos criar uma versão aperfeiçoada do jogo RPS+ para que a escolha do agente seja revelada depois que você fizer a sua escolha, pois, afinal, é a adivinhação e o resultado surpreendente que tornam divertidos jogos como o Pedra, Papel e Tesoura.</p><p>No seu computador local, abra o <a href="https://code.visualstudio.com/download">Visual Studio Code</a> e abra um novo terminal.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" style="width: 100%;" /><p>No terminal recém-aberto, execute o seguinte comando para clonar o repositório Elasticsearch Labs:</p><pre><code>git clone https://github.com/elastic/elasticsearch-labs</code></pre><p>Execute o seguinte <em>cd </em>comando para alterar o diretório para o diretório elasticsearch-labs:</p><pre><code>cd elasticsearch-labs</code></pre><p>Em seguida, execute o seguinte comando para abrir o repositório no Visual Studio Code:</p><pre><code>code .</code></pre><p>No Visual Studio File Explorer, expanda as pastas <em>contenting-blog-content</em> e <em>agent-builder-a2a-strands-agents</em> e abra o arquivo <em>elastic_agent_builder_a2a_rps+.py.</em> Veja a aparência do arquivo aberto no Visual Studio Code:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" style="width: 100%;" /><p>Aqui está o conteúdo de <em>elastic_agent_builder_a2a_rps+.py </em>que você deve ver no seu editor de texto:</p><pre><code>import asyncio
from dotenv import load_dotenv
from uuid import uuid4
import httpx
import os
import random
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
from a2a.types import Message, Part, Role, TextPart

DEFAULT_TIMEOUT = 60  # set request timeout to 1 minute


def create_message(*, role: Role = Role.user, text: str, context_id=None) -> Message:
    return Message(
        kind="message",
        role="user",
        parts=[Part(TextPart(kind="text", text=text))],
        message_id=uuid4().hex,
        context_id=context_id,
    )


async def main():
    load_dotenv()
    a2a_agent_host = os.getenv("ES_AGENT_URL")
    a2a_agent_key = os.getenv("ES_API_KEY")
    custom_headers = {"Authorization": f"ApiKey {a2a_agent_key}"}

    async with httpx.AsyncClient(
        timeout=DEFAULT_TIMEOUT, headers=custom_headers
    ) as httpx_client:
        # Get agent card
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=a2a_agent_host)
        agent_card = await resolver.get_agent_card(
            relative_card_path="/rps_plus_agent.json"
        )
        # Create client using factory
        config = ClientConfig(
            httpx_client=httpx_client,
            streaming=True,
        )
        factory = ClientFactory(config)
        client = factory.create(agent_card)
        # Use the client to communicate with the agent
        print("\nSending 'start game' message to Elastic A2A agent...")
        random_game_object = random.randint(1, 5)
        msg = create_message(text=f"start with game object {random_game_object}")
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                context_id = event.context_id
                response_complete = event.parts[0].root.text
                # Get agent choice from the first line of the response
                parsed_response = response_complete.split("\n", 1)
                agent_choice = parsed_response[0]
                print(parsed_response[1])
        # User choice sent for game results from the agent
        prompt = input("Your Choice  : ")
        msg = create_message(text=prompt, context_id=context_id)
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                print(f"Agent Choice : {agent_choice}")
                print(event.parts[0].root.text)


if __name__ == "__main__":
    asyncio.run(main())</code></pre><p>Vamos revisar o que está acontecendo nesse código. Começando pelo método <em><code>main()</code></em>, o código começa acessando as variáveis de ambiente para a URL do agente e a Chave da API. Depois, usamos esses valores para criar um <code><em>httpx</em> client</code> que podemos usar para obter o cartão de agente para o agente. O cliente então usa os detalhes do cartão do agente para enviar uma solicitação &quot;iniciar jogo&quot; ao agente. Uma coisa interessante a notar aqui é que incluímos um valor <code>random_game_object</code> como parte do pedido <code>&quot;start game&quot;</code>. Esse valor é um número aleatório gerado com o módulo <em>aleatório</em> da biblioteca padrão do Python. A razão para fazer isso é que os poderosos LLMs (que possibilitam agentes de IA) não são bons em aleatoriedade. Não tema, Python vem pra salvar.</p><p>Continuando com o código, quando o agente responde à solicitação &quot;iniciar jogo&quot;, o código remove a seleção de objeto de jogo do agente e a salva na variável <em>agent_choice</em>. O restante da resposta é exibido como texto para o usuário final. Em seguida, o usuário é solicitado a fornecer a entrada da sua escolha de objeto de jogo, que é enviada ao agente. O código então exibe a escolha do objeto de jogo do agente junto com a determinação final do agente sobre o resultado do jogo.</p><h2>Definindo a URL do seu agente e a chave de API como variáveis de ambiente</h2><p>Como o app de exemplo estará rodando no seu computador local, para nos comunicarmos com nosso agente Agent Builder, precisamos fornecer ao Strands Agents SDK uma URL A2A e uma chave API para o agente. O exemplo de app usa um arquivo chamado <em>.env</em> para armazenar esses valores.</p><p>Faça uma cópia do <em>arquivo env.example</em> e nomeie o novo arquivo como <em>.env</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" style="width: 100%;" /><p>Volte para o Elastic Agent Builder, onde podemos obter os dois valores que precisamos.</p><p>Selecione <strong>Exibir todas as ferramentas</strong> no menu de ação do Agent Builder no canto superior direito da página.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" style="width: 100%;" /><p>Clique no menu suspenso <strong>Servidor MCP</strong> na parte superior da página Ferramentas e selecione <strong>Copiar URL do Servidor MCP.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" style="width: 100%;" /><p>Cole o <strong>URL do servidor MCP</strong> no arquivo <em>.env</em> como um substituto para o valor do espaço reservado <strong>&lt;YOUR-ELASTIC-AGENT-BUILDER-URL&gt; </strong>. Agora precisamos fazer uma atualização no URL, ou seja, substituir o texto final “mcp” por “a2a”, pois o <a href="https://a2a-protocol.org/">protocolo A2A</a> é o que o Agent Strands SDK usará para se comunicar com o agente em execução no Elastic Agent Builder.</p><p>A URL editada deve ficar assim:</p><pre><code>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a</code></pre><p>Outro valor que precisamos obter enquanto estamos aqui no Elastic Cloud é uma chave API. Clique em <strong>Elasticsearch </strong>na navegação de nível superior.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" style="width: 100%;" /><p>Clique no <strong>botão Copiar chave API </strong>para copiar a chave API.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" style="width: 100%;" /><p>Agora, de volta ao Visual Studio Code, cole a chave API no <em>.env</em> para substituir o texto provisório <strong>&lt;YOUR-ELASTIC-API-KEY&gt; </strong>. Seu arquivo <em>.env</em> deve ficar assim:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" style="width: 100%;" /><h2>Execute o app de exemplo</h2><p>Abra um novo terminal no Visual Studio Code.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" style="width: 100%;" /><p>Comece executando o seguinte comando <em>cd</em> no terminal:</p><pre><code>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents</code></pre><p>Execute o seguinte comando para criar um ambiente virtual Python.</p><pre><code>python -m venv .venv</code></pre><p>Dependendo do sistema operacional do seu computador local, execute o seguinte comando para ativar o ambiente virtual.</p><ul><li>MacOS/Linux</li></ul><pre><code>source .venv/bin/activate</code></pre><ul><li>Windows</li></ul><pre><code>.venv\Scripts\activate</code></pre><p>O app de exemplo usa o Strands Agents SDK e agora estamos no ponto em que precisamos instalá-lo. Execute o seguinte comando para instalar o Strands Agents SDK junto com todas as dependências necessárias da biblioteca Python.</p><pre><code>pip install -r requirements.txt</code></pre><p>Hora de liberar a plataforma de lançamento e começar a contagem regressiva. Estamos prontos para executar este app. Afastem-se. Vamos executá-lo usando o seguinte comando:</p><pre><code>python elastic_agent_builder_a2a_rps+.py</code></pre><p>Você deve ser desafiado com uma partida de RPS+. Parabéns e boa sorte!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" style="width: 100%;" /><h2>Crie seus aplicativos de IA com contexto relevante</h2><p>Construir um Agente de IA agora é uma habilidade disponível na sua caixa de ferramentas. E você já viu como é fácil usar agentes Elastic Agent Builder via A2A em frameworks de desenvolvimento de agentes como o Strands Agents SDK. <a href="https://cloud.elastic.co/registration?utm_source=agentic-ai-category&amp;utm_medium=search-labs&amp;utm_campaign=agent-builder">Experimente a Elastic</a> para criar agentes de IA conectados ao contexto relevante em seus dados personalizados.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introdução do suporte ao Elasticsearch no Google MCP Toolbox for Databases]]></title>
            <description><![CDATA[Veja como o suporte ao Elasticsearch agora está disponível no Google MCP Toolbox for Databases e adote as ferramentas ES|QL para integrar seu índice com segurança a qualquer cliente MCP.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/google-mcp-toolbox-elasticsearch-support</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/google-mcp-toolbox-elasticsearch-support</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Enrico Zimuel, Laurent Saint-Félix]]></dc:creator>
            <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, vamos explicar como usar o Google MCP Toolbox com o <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a> para construir uma ferramenta simples de extração de informações de um índice do Elasticsearch.</p><p>Recentemente, contribuímos para o projeto open source <a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a>, adicionando suporte ao Elasticsearch como banco de dados.</p><p>Com esse novo recurso, agora você pode usar o Google MCP Toolbox para se conectar ao Elasticsearch e &quot;conversar&quot; diretamente com seus dados.</p><h2>Elasticsearch</h2><p>Precisamos ter uma instância do Elasticsearch em execução. Você pode ativar uma avaliação gratuita no <a href="https://www.elastic.co/cloud">Elastic Cloud</a> ou instalá-lo localmente usando o <a href="https://github.com/elastic/start-local">script start-local</a>:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Isso instalará o Elasticsearch e o Kibana no seu computador e gerará uma chave API para ser usada na configuração do Google MCP Toolbox.</p><p>A chave API será mostrada como saída do comando anterior e armazenada em um arquivo .env na pasta elastic-start-local.</p><h2>Instale o conjunto de dados de exemplo</h2><p>Após a instalação, você pode fazer login no Kibana usando o nome do usuário <em>elastic</em> e a senha gerada pelo script start-local (armazenada em um arquivo .env).</p><p>Você pode instalar o conjunto de dados de <strong>pedidos de comércio eletrônico </strong>disponível no Kibana. Inclui um único índice chamado <strong>kibana_sample_data_ecommerce</strong> contendo informações sobre 4.675 pedidos de um website de comércio eletrônico. Para cada pedido, temos as seguintes informações:</p><ul><li>Informações do cliente (nome, ID, data de nascimento, e-mail, etc.)</li><li>Data do pedido</li><li>ID do pedido</li><li>Produtos (lista de todos os produtos com preço, quantidade, ID, categoria, desconto, etc.)</li><li>SKU</li><li>Preço total (sem impostos, com impostos)</li><li>Quantidade total</li><li>Informações geográficas (cidade, país, continente, localização, região)</li></ul><p>Para instalar os dados de exemplo, abra a página <strong>Integrações</strong> no Kibana (busque por “Integração” na barra de busca superior) e instale os “Dados de Exemplo”. Confira os detalhes na documentação aqui: <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a>.</p><p>O objetivo deste artigo é mostrar como é fácil configurar o Google MCP Toolbox para se conectar ao Elasticsearch e interagir com o <strong>índice kibana_sample_data_ecommerce</strong> usando linguagem natural.</p><h2>Google MCP Toolbox</h2><p>O Google MCP Toolbox é um servidor MCP open source projetado para facilitar a interação de aplicações e agentes de IA com bancos de dados de forma segura e eficiente. Antes chamado de &quot;GenAI Toolbox for Databases&quot;, o projeto foi renomeado após adotar total compatibilidade com o <a href="https://www.anthropic.com/news/model-context-protocol">Protocolo de Contexto de Modelo</a> (MCP). Seu objetivo é eliminar o trabalho pesado tradicionalmente exigido ao conectar agentes a bancos de dados, lidando com agrupamento de conexões, autenticação, observabilidade e outras preocupações operacionais nos bastidores.</p><p>Essencialmente, o Toolbox permite que desenvolvedores definam ferramentas reutilizáveis e de alto nível que encapsulam interações com bancos de dados. Essas ferramentas podem então ser invocadas por qualquer cliente que cumpra o MCP — como um agente de IA — sem exigir que o cliente implemente consultas SQL de baixo nível ou gerencie conexões de banco de dados. Essa abordagem reduz drasticamente a quantidade de código padrão necessário para construir agentes conscientes de banco de dados, tornando possível integrar operações avançadas de dados em apenas algumas linhas de lógica de aplicação. Uma vez definida uma ferramenta, ela pode ser compartilhada entre vários agentes, frameworks ou linguagens (Figura 1).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" style="width: 100%;" /><p>Uma grande vantagem de usar o Toolbox é o modelo de segurança integrado. Fluxos de autenticação, como OAuth2 e OIDC, são aceitos de forma nativa, permitindo que os desenvolvedores evitem manipular ou armazenar credenciais confidenciais de bancos de dados em agentes. A plataforma também fornece recursos de observabilidade, incluindo métricas e rastreamento, no OpenTelemetry, que é essencial para depuração, monitoramento e implantações de produção. No geral, o MCP Toolbox serve como uma interface unificada, segura e extensível para interagir com seus dados de qualquer sistema habilitado pelo MCP.</p><h2>Como instalar o MCP Toolbox</h2><p>Você pode instalar o servidor MCP Toolbox no Linux usando o seguinte comando:</p><pre><code>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox</code></pre><p>Para instalá-lo no macOS ou Windows, siga as instruções detalhadas <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">aqui</a>.</p><h2>Configure o Toolbox para Elasticsearch</h2><p>Para configurar o MCP Toolbox para Elasticsearch, precisamos criar um arquivo <strong>tools.yaml</strong> , conforme segue:</p><pre><code>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: <insert-here-api-key>

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders</code></pre><p>Você precisa trocar o valor <strong>&lt;insert-here-api-key&gt;</strong> por uma chave API válida do Elasticsearch. Se você estiver rodando o Elasticsearch localmente usando o start-local, pode encontrar a chave API no arquivo .env gerado pelo start-local, sob a variável <strong>ES_LOCAL_API_KEY</strong> . Se você estiver usando o Elastic Cloud, poderá gerar uma chave de API seguindo o procedimento descrito <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">aqui</a>.</p><p>As ferramentas anteriores contêm a seguinte consulta ES|QL para Elasticsearch:</p><pre><code>FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name)</code></pre><p>Se você não conhece o ES|QL, é uma linguagem de consulta desenvolvida pela Elastic, semelhante ao SQL, que pode ser usada para buscar em um ou mais índices. Saiba mais sobre ES|QL na documentação oficial <a href="https://www.elastic.co/docs/reference/query-languages/esql">aqui</a>.</p><p>A consulta acima busca todos os pedidos armazenados no <strong>índice kibana_sample_data_ecommerce</strong> que contêm o nome do cliente especificado, usando o parâmetro <strong>?name</strong> (o ponto de interrogação indica um parâmetro).</p><p>O nome do cliente é definido na configuração YAML anterior usando a string de tipo e a descrição &quot;O nome do cliente&quot;.</p><p>Essa ferramenta pode ser usada para responder a perguntas sobre os pedidos de um cliente - por exemplo: <em>Quantos pedidos o cliente Foo fez em outubro de 2025?</em></p><p>As descrições das ferramentas e seus parâmetros são essenciais para extrair as informações relevantes da solicitação em linguagem natural do usuário. Essa extração é realizada usando o recurso de <strong>chamada de função</strong> de um modelo de linguagem grande (LLM). Na prática, um LLM pode determinar qual função (ferramenta) precisa ser executada para obter as informações necessárias, juntamente com os parâmetros apropriados para essa função.</p><p>Para saber mais sobre chamadas de função, sugerimos o artigo <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">Chamadas de função do OpenAI com Elasticsearch</a>, de Ashish Tiwari.</p><h2>Execute o servidor Toolbox</h2><p>Você pode executar o MCP Toolbox usando o arquivo tools.yaml anterior com o seguinte comando:</p><pre><code>./toolbox --tools-file tools.yaml --ui</code></pre><p>O parâmetro<strong> –ui</strong> executa uma aplicação web em <a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a> (Figura 2).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" style="width: 100%;" /><p>Você pode selecionar <strong>Ferramentas</strong> &gt; <strong>customer-orders</strong> e inserir o nome do cliente no campo <strong>Nome</strong> do parâmetro (por exemplo, Gwen Sanders) e clicar no botão <strong>Executar</strong>. Você deve ver uma resposta JSON conforme a Figura 3.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" style="width: 100%;" /><p>A configuração está concluída, e o MCP Toolbox pode executar a ferramenta <strong>customer-orders</strong> para se comunicar com o Elasticsearch, rodando o ES|QL.</p><h2>Usando o MCP Toolbox com Gemini CLI</h2><p>Podemos usar qualquer cliente MCP para nos comunicar com o MCP Toolbox for Databases. Por exemplo, podemos usar o <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>, uma ferramenta de linha de comando, para usar o Gemini. Você pode instalar o Gemini CLI seguindo as instruções descritas <a href="https://geminicli.com/docs/get-started/installation/">aqui</a>.</p><p>Gemini CLI oferece uma extensão pré-configurada para MCP Toolbox, disponível em <a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>. Você pode instalar esta extensão executando o seguinte comando:</p><pre><code>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox</code></pre><p>Após a instalação, você precisa ir para o diretório em que armazenou o arquivo de configuração tools.yaml do MCP Toolbox e executar a CLI do Gemini da seguinte forma (essa etapa é necessária para que a CLI do Gemini seja configurada automaticamente com o MCP Toolbox):</p><pre><code>gemini</code></pre><p>Você deve ver uma saída conforme a Figura 4.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" style="width: 100%;" /><p>Você pode verificar se a MCP Toolbox está conectada usando o seguinte comando:</p><pre><code>/mcp list</code></pre><p>Você deve ver a <strong>mcp_toolbox</strong> com as ferramentas<strong> de pedidos de clientes</strong> listadas (Figura 5).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" style="width: 100%;" /><p>Se o MCP Toolbox estiver conectado à interface de comando Gemini, agora podemos tentar fazer algumas perguntas, como: &quot;<em>Me dê os pedidos da cliente Gwen Sanders</em>.&quot; A CLI Gemini então solicitará permissão para executar a ferramenta de pedidos do cliente ao servidor mcp_toolbox (veja a Figura 6).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" style="width: 100%;" /><p>Após a confirmação, a CLI Gemini executará a solicitação para a MCP Toolbox, recebendo uma resposta JSON como resultado e usando para formatar a resposta (Figura 7).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" style="width: 100%;" /><p>A resposta da Gemini CLI emitirá um relatório indicando que Gewn Sanders fez apenas um pedido de 2 produtos, totalizando 132 euros.</p><h2>SDKs do MCP Toolbox</h2><p>O Google MCP Toolbox também oferece um SDK para acessar todas as funcionalidades de um programa escrito em Go, Python e Javascript.</p><p>Por exemplo, o Python SDK está disponível no Github na seguinte página: <a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python.</a></p><p>Precisamos criar um agente simples para conectar à MCP Toolbox. Precisamos instalar os seguintes pacotes:</p><pre><code>pip install toolbox-core
pip install google-adk</code></pre><p>E crie um novo projeto de agente usando o comando a seguir:</p><pre><code>adk create my_agent</code></pre><p>Isso criará um novo diretório chamado <strong>my_agent</strong> com um <strong>arquivo agent.py</strong>.</p><p>Atualize <strong>my_agent/agent.py</strong> com o seguinte conteúdo para conectar ao Toolbox:</p><pre><code>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")</code></pre><p>Crie um arquivo <strong>.env</strong> com sua chave API do Google:</p><pre><code>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' > my_agent/.env</code></pre><p>Por fim, podemos executar o agente e observar os resultados. Para executar o agente, você pode executar o seguinte comando:</p><pre><code>adk run my_agent</code></pre><p>Ou pode servi-lo por meio de uma interface web:</p><pre><code>adk web --port 8000</code></pre><p>Em ambos os casos, você pode interagir com a MCP Toolbox usando uma interface de perguntas e respostas. Por exemplo, você pode fazer a pergunta anterior: <em>Me dê os pedidos da cliente Gwen Sanders</em>.</p><p>Para saber mais sobre os diferentes SDKs, consulte <a href="https://googleapis.github.io/genai-toolbox/sdks/">esta página de documentação</a>.</p><h2>Conclusão</h2><p>Neste artigo, demonstramos a integração com o Elasticsearch para o Google MCP Toolbox for Databases. Usando um arquivo de configuração YAML simples, podemos definir um conjunto de ferramentas que traduzem perguntas de linguagem natural em consultas do Elasticsearch usando a linguagem ES|QL.</p><p>Mostramos como interagir com os conjuntos de dados kibana_sample_data_ecommerce, que contém pedidos de um website de e-commerce. Com esse arquivo de configuração, basta executar o servidor MCP Toolbox e conectar a ele a partir de qualquer cliente MCP.</p><p>Por fim, demonstramos como usar o Gemini CLI como cliente para conectar-se ao MCP Toolbox for Databases e consultar os dados de comércio eletrônico armazenados no Elasticsearch. Executamos uma consulta em linguagem natural para obter informações sobre pedidos de um cliente específico identificado pelo nome.</p><p>À medida que o ecossistema MCP continua crescendo, esse padrão — definições leves de ferramentas apoiadas por uma infraestrutura segura e pronta para produção — gera novas oportunidades para criar agentes cada vez mais capazes e com reconhecimento de dados com o mínimo esforço. Seja experimentando localmente com os conjuntos de dados de amostra da Elastic ou integrando capacidades de buscar em uma aplicação maior, o MCP Toolbox tem uma base confiável e extensível para interagir com os dados do Elasticsearch usando linguagem natural.</p><p>Para saber mais sobre o desenvolvimento de aplicações de IA agêntica, leia o artigo <a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Criando fluxos de trabalho de IA agêntica com Elasticsearch</a>, de Anish Mathur e Dana Juratoni.</p><p>Para saber mais informações sobre o Google MCP Toolbox, visite <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Avaliação da relevância de consultas de pesquisa com listas de julgamento]]></title>
            <description><![CDATA[Saiba como criar listas de julgamento para avaliar objetivamente a relevância das consultas de pesquisa e melhorar métricas de desempenho, como recall, para testes de buscas escaláveis no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/judgment-lists-search-query-relevance-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/judgment-lists-search-query-relevance-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
            <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25478d0ffb228afd5d65d82312998ec1c299c565-700x490.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Os desenvolvedores que trabalham em mecanismos de busca frequentemente se deparam com o mesmo problema: a equipe de negócios não está satisfeita com uma busca específica porque os documentos que eles esperam ver na parte de cima dos resultados da busca aparecem em terceiro ou quarto lugar na lista de resultados.</p><p>No entanto, quando você resolve esse problema, acaba com outras consultas porque não pôde testar todos os casos manualmente. Mas como você ou sua equipe de QA podem testar se uma mudança em uma consulta tem efeito dominó em outras? Ou, mais importante ainda, como você pode ter certeza de que suas mudanças realmente melhoraram uma consulta?</p><h2>Rumo a uma avaliação sistemática</h2><p>É aqui que as listas de julgamento se tornam úteis. Em vez de depender de testes manuais e subjetivos toda vez que você faz uma alteração, você pode definir um conjunto fixo de consultas relevantes para seu caso de negócio, juntamente com os resultados relevantes.</p><p>Esse conjunto se torna sua referência. Toda vez que você implementa uma mudança, você a usa para avaliar se você buscou uma melhoria ou não.</p><p>O valor dessa abordagem é:</p><ul><li><strong>Elimina a incerteza</strong>: você não precisa mais se perguntar se suas alterações afetam outras consultas; os dados dirão isso a você.</li><li><strong>Interrompe os testes manuais</strong>: assim que os conjuntos de julgamento são registrados, o teste é automático.</li><li><strong>Dá suporte à mudanças</strong>: você pode apresentar métricas claras que sustentam os benefícios de uma mudança.</li></ul><h2>Como começar a construir sua lista de julgamentos</h2><p>Uma das formas mais fáceis de começar é pegar uma consulta representativa e selecionar manualmente os documentos relevantes. Existem duas maneiras de fazer esta lista:</p><ul><li><strong>Julgamentos binários:</strong> cada documento associado a uma consulta recebe uma <strong>marcação simples</strong>: <em>relevante</em> (geralmente com uma pontuação de “1”) e não-relevante (“0”).</li><li><strong>Julgamentos graduados:</strong> aqui, cada documento recebe uma pontuação com diferentes níveis. Por exemplo: definir uma escala de 0 a 4, semelhante à <a href="https://en.wikipedia.org/wiki/Likert_scale">Escala Likert</a>, onde 0 = &quot;nada relevante&quot; e 4 = &quot;totalmente relevante&quot;, com variações como &quot;relevante&quot;, &quot;um pouco relevante&quot; etc.</li></ul><p>Julgamentos binários funcionam bem quando a intenção de buscar tem limites claros: esse documento deve estar nos resultados ou não?</p><p>Julgamentos graduados são mais úteis quando há áreas cinzentas: alguns resultados são melhores que outros, então você pode ter resultados &quot;muito bons&quot;, &quot;bons&quot; e &quot;inúteis&quot; e usar métricas que valorizam a ordem dos resultados e o feedback do usuário. No entanto, as escalas graduadas também introduzem desvantagens: diferentes avaliadores podem usar os níveis de pontuação de maneira diferente, o que torna os julgamentos menos consistentes. E porque as métricas graduadas dão mais peso às pontuações mais altas, mesmo uma pequena mudança (como classificar algo com 3 em vez de 4) pode criar uma mudança muito maior na métrica do que o avaliador pretendia. Essa subjetividade adicional torna os julgamentos graduados mais complicados e difíceis de gerenciar ao longo do tempo.</p><h2>Preciso classificar os documentos eu mesmo?</h2><p>Não necessariamente, pois existem diferentes maneiras de criar sua lista de julgamentos, cada uma com suas próprias vantagens e desvantagens:</p><ul><li><strong>Julgamentos explícitos:</strong> aqui, os SMEs analisam cada consulta/documento e decidem manualmente se é relevante e qual a dimensão da relevância. Embora isso ofereça qualidade e controle, tem menos escalabilidade.</li><li><strong>Julgamentos implícitos:</strong> com esse método, você infere os documentos relevantes com base no comportamento real dos usuários, como cliques, taxa de rejeição e compras, entre outros. Essa abordagem permite coletar dados automaticamente, mas pode ser tendenciosa. Por exemplo, os usuários tendem a clicar mais vezes nos resultados principais, mesmo que não sejam relevantes.</li><li><strong>Julgamentos gerados por IA:</strong> essa última opção utiliza modelos (como LLMs) para avaliar automaticamente consultas e documentos, chamados <a href="https://en.wikipedia.org/wiki/LLM-as-a-Judge">LLM como juiz</a>. É rápido e fácil de redimensionar, mas a qualidade dos dados depende da qualidade do modelo que você está usando e de como os dados de treinamento do LLM se alinham aos seus <a href="http://interests.as/">interesses</a> comerciais. Assim como acontece com as notas humanas, os LLMs como juiz podem apresentar os próprios preconceitos ou inconsistências, por isso é importante validar o resultado em relação a um conjunto menor de julgamentos confiáveis. Modelos LLM são probabilísticos por natureza, então não é incomum ver um modelo LLM dando diferentes graus ao mesmo resultado, independentemente de definir o parâmetro de <a href="https://www.ibm.com/think/topics/llm-temperature">temperatura</a> como 0.</li></ul><p>A seguir, apresentamos algumas recomendações para escolher o melhor método para criar seu conjunto de julgamentos:</p><ul><li>Decida a importância de alguns recursos que somente os usuários possam avaliar de forma adequada (como preço, marca, idioma, estilo e detalhes do produto). Se eles forem importantes, você precisará de <strong>julgamentos explícitos</strong> para pelo menos uma parte da sua <em>lista de julgamentos</em>.</li><li>Use <strong>julgamentos implícitos</strong> quando seu mecanismo de busca já tiver tráfego suficiente para que você possa usar cliques, conversões e métricas de tempo persistentes para detectar tendências de uso. Você ainda deve interpretá-los com cuidado, comparando-os com seus conjuntos de julgamento explícitos para evitar qualquer viés (por exemplo: os usuários tendem a clicar nos resultados mais bem classificados com mais frequência, mesmo que os resultados com classificação inferior sejam mais relevantes)</li></ul><p>Para resolver isso, técnicas de posicionamento de debiasing ajustam ou reponderam os dados de cliques para refletir melhor o verdadeiro interesse do usuário. Algumas abordagens incluem:</p><ul><li><strong>Reorganização de resultados</strong>: altere a ordem dos resultados de busca para um subconjunto de usuários a fim de estimar como a posição afeta os cliques.</li><li><strong>Os modelos de clique </strong>incluem<strong> </strong><a href="https://wiki.math.uwaterloo.ca/statwiki/index.php?title=a_Dynamic_Bayesian_Network_Click_Model_for_web_search_ranking">Rede bayesiana dinâmica <strong>DBN</strong></a>, <a href="https://rsrikant.com/papers/kdd10.pdf">Modelo de Navegação do Usuário <strong>UBM</strong></a>. Esses modelos estatísticos estimam que a probabilidade de um clique reflete o interesse real em vez de apenas posição, usando padrões como rolagem, tempo de espera, sequência de cliques e retorno à página de resultados.</li></ul><h2>Exemplo: app de avaliação de filmes</h2><h3>Pré-requisitos</h3><p>Para executar este exemplo, você precisa de um cluster Elasticsearch 8.x em execução, <a href="https://www.elastic.co/downloads/elasticsearch">localmente</a> ou <a href="https://www.elastic.co/cloud/cloud-trial-overview">no Elastic Cloud</a> (hospedado ou sem servidor), e acesso à <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis">REST API</a> ou ao Kibana.</p><p>Pense em um app no qual os usuários possam carregar as opiniões sobre filmes e também buscar filmes para assistir. Como os textos são escritos pelos próprios usuários, eles podem ter erros de digitação e muitas variações em termos de expressão. Portanto, é fundamental que o mecanismo de busca seja capaz de interpretar essa diversidade e fornecer resultados úteis para os usuários.</p><p>Para poder iterar consultas sem impactar o comportamento geral de busca, a equipe de negócios da sua empresa criou o seguinte conjunto de julgamento binário, baseado nas buscas mais frequentes:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consulta</td><td style="border: 1px solid #ccc; padding: 4px;">DocID</td><td style="border: 1px solid #ccc; padding: 4px;">Texto</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Performance de DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">A atuação de DiCaprio em O Regresso foi de tirar o fôlego.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Performance de DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">A Origem mostra Leonardo DiCaprio em um dos papéis mais icônicos que ele já fez.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Performance de DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">Brad Pitt entrega uma atuação sólida neste thriller policial.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Performance de DiCaprio</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td><td style="border: 1px solid #ccc; padding: 4px;">Uma aventura cheia de ação com efeitos visuais impressionantes.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">filmes tristes que fazem você chorar</td><td style="border: 1px solid #ccc; padding: 4px;">doc5</td><td style="border: 1px solid #ccc; padding: 4px;">Uma história comovente de amor e perda que me fez chorar muito.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">filmes tristes que fazem você chorar</td><td style="border: 1px solid #ccc; padding: 4px;">doc6</td><td style="border: 1px solid #ccc; padding: 4px;">Um dos filmes mais tristes já feitos — traga lenços!</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">filmes tristes que fazem você chorar</td><td style="border: 1px solid #ccc; padding: 4px;">doc7</td><td style="border: 1px solid #ccc; padding: 4px;">Uma comédia leve que vai fazer rir</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">filmes tristes que fazem você chorar</td><td style="border: 1px solid #ccc; padding: 4px;">doc8</td><td style="border: 1px solid #ccc; padding: 4px;">Uma saga de ficção científica épica repleta de ação e emoção.</td>
                    </tr>
              </tbody>
            </table>
          <p>Criando o índice:</p><pre><code>PUT movies
{
  "mappings": {
    "properties": {
      "text": {
        "type": "text"
      }
    }
  }
}</code></pre><p>SOLICITAÇÃO em massa:</p><pre><code>POST /movies/_bulk
{ "index": { "_id": "doc1" } }
{ "text": "DiCaprio performance in The Revenant was breathtaking." }
{ "index": { "_id": "doc2" } }
{ "text": "Inception shows Leonardo DiCaprio in one of his most iconic roles." }
{ "index": { "_id": "doc3" } }
{ "text": "Brad Pitt delivers a solid performance in this crime thriller." }
{ "index": { "_id": "doc4" } }
{ "text": "An action-packed adventure with stunning visual effects." }
{ "index": { "_id": "doc5" } }
{ "text": "A heartbreaking story of love and loss that made me cry for hours." }
{ "index": { "_id": "doc6" } }
{ "text": "One of the saddest movies ever made -- bring tissues!" }
{ "index": { "_id": "doc7" } }
{ "text": "A lighthearted comedy that will make you laugh." }
{ "index": { "_id": "doc8" } }
{ "text": "A science-fiction epic full of action and excitement." }</code></pre><p>Abaixo está a consulta Elasticsearch que o aplicativo está usando:</p><pre><code>GET movies/_search
{
 "query": {
   "match": {
     "text": {
       "query": "DiCaprio performance",
       "minimum_should_match": "100%"
     }
   }
 }
}</code></pre><h3>Do julgamento às métricas</h3><p>Sozinho, as listas de julgamento não fornecem muitas informações; eles são apenas uma expectativa dos resultados das nossas consultas. O momento importante deles é quando os usamos para calcular métricas objetivas para medir nosso desempenho na busca.</p><p>Hoje em dia, a maioria das métricas populares inclui</p><ul><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-precision">Precisão</a>: </strong>mede a proporção de resultados relevantes em todos os resultados de busca.</li><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#k-recall">Recall</a>: </strong>mede a proporção de resultados relevantes que o mecanismo de busca encontrou entre x resultados.</li><li><strong><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_discounted_cumulative_gain_dcg">Ganho cumulativo descontado (DCG):</a> </strong>mede a qualidade do ranking do resultado, considerando que os resultados mais relevantes devem estar no topo.</li><li><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval#_mean_reciprocal_rank"><strong>Classificação Recíproca Média (MRR):</strong></a> mede a posição do primeiro resultado relevante. Quanto mais alto na lista, maior a pontuação.</li></ul><p>Usando o mesmo app de avaliação de filmes como exemplo, calcularemos a métrica de recordação para ver se há alguma informação que está sendo omitida em nossas consultas.</p><p>No Elasticsearch, podemos usar as <em>listas de julgamentos</em> para calcular métricas via <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/search-rank-eval">API de avaliação de classificação</a>. Essa API recebe como entrada a lista de julgamentos, a consulta e a métrica que você deseja avaliar e retorna um valor, que é uma comparação do resultado da consulta com a lista de julgamentos.</p><p>Vamos executar a lista de julgamento para as duas consultas que temos:</p><pre><code>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry",
             "minimum_should_match": "100%"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}</code></pre><p>Vamos usar dois pedidos para _rank_eval: um para a consulta do DiCaprio e outro para filmes tristes. Cada solicitação inclui uma consulta e a lista de julgamento (avaliações). Não precisamos classificar todos os documentos, pois aqueles que não estão incluídos nas classificações são considerados sem julgamento. Para realizar os cálculos, o sistema considera apenas o &quot;conjunto relevante&quot;, ou seja, os documentos que são considerados relevantes na avaliação.</p><p>Nesse caso, a consulta do DiCaprio tem resultado de 1, enquanto os filmes tristes receberam 0 resultados. Isso significa que na primeira consulta, conseguimos obter todos os resultados relevantes, enquanto na segunda consulta, não obtivemos nenhum resultado. Portanto, a média de recall é de 0,5.</p><pre><code>{
 "metric_score": 0.5,
 "details": {
   "dicaprio-performance": {
     "metric_score": 1,
     "unrated_docs": [],
     "hits": [
       {
         "hit": {
           "_index": "movies",
           "_id": "doc1",
           "_score": 2.4826927
         },
         "rating": 1
       },
       {
         "hit": {
           "_index": "movies",
           "_id": "doc2",
           "_score": 2.0780432
         },
         "rating": 1
       }
     ],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 2,
         "relevant_docs": 2
       }
     }
   },
   "sad-movies": {
     "metric_score": 0,
     "unrated_docs": [],
     "hits": [],
     "metric_details": {
       "recall": {
         "relevant_docs_retrieved": 0,
         "relevant_docs": 2
       }
     }
   }
 },
 "failures": {}
}</code></pre><p>Talvez estejamos sendo muito rigorosos com o parâmetro <strong>minimum_should_match </strong>, já que ao exigir que 100% das palavras da consulta estejam nos documentos, provavelmente estamos deixando de fora os resultados relevantes. Vamos remover o parâmetro <strong>minimum_should_match</strong> para que um documento seja considerado relevante se apenas uma palavra na consulta seja encontrada nele.</p><pre><code>POST /movies/_rank_eval
{
 "requests": [
   {
     "id": "dicaprio-performance",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "DiCaprio performance"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc1",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc2",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc3",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc4",
         "rating": 0
       }
     ]
   },
   {
     "id": "sad-movies",
     "request": {
       "query": {
         "match": {
           "text": {
             "query": "sad movies that make you cry"
           }
         }
       }
     },
     "ratings": [
       {
         "_index": "movies",
         "_id": "doc5",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc6",
         "rating": 1
       },
       {
         "_index": "movies",
         "_id": "doc7",
         "rating": 0
       },
       {
         "_index": "movies",
         "_id": "doc8",
         "rating": 0
       }
     ]
   }
 ],
 "metric": {
   "recall": {
     "k": 10,
     "relevant_rating_threshold": 1
     }
 }
}</code></pre><p>Como você pode ver, ao remover o parâmetro <strong>minimum_should_match</strong> em uma das duas consultas, agora obtemos uma taxa de acerto média de 1 em ambas.</p><pre><code>{
  "metric_score": 1,
  "details": {
    "dicaprio-performance": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc1",
            "_score": 2.0661702
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc3",
            "_score": 0.732218
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc2",
            "_score": 0.6271719
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    },
    "sad-movies": {
      "metric_score": 1,
      "unrated_docs": [],
      "hits": [
        {
          "hit": {
            "_index": "movies",
            "_id": "doc7",
            "_score": 2.1307156
          },
          "rating": 0
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc5",
            "_score": 1.3160692
          },
          "rating": 1
        },
        {
          "hit": {
            "_index": "movies",
            "_id": "doc6",
            "_score": 1.190063
          },
          "rating": 1
        }
      ],
      "metric_details": {
        "recall": {
          "relevant_docs_retrieved": 2,
          "relevant_docs": 2
        }
      }
    }
  },
  "failures": {}
}</code></pre><p>Em resumo, remover a cláusula minimum_should_match: 100% nos permite ter um recall perfeito para ambas as consultas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/24d055da4348c63827ba7046fe8cafb6f47cadd8-546x628.png" style="width: 100%;" /><p>Conseguimos! Certo?</p><p>Não tão depressa!</p><p>Ao melhorar o recall, abrimos as portas para uma gama maior de resultados. No entanto, cada ajuste implica uma contrapartida. É por isso que definimos casos de teste completos, usando diferentes métricas para avaliar as mudanças.</p><p>Usar listas de julgamento e métricas evita que você fique às cegas ao fazer alterações, pois agora você tem dados para respaldá-las. A validação não é mais manual e repetitiva, e você pode testar as mudanças em mais de um caso de uso. Além disso, o teste A/B permite que você teste ao vivo qual configuração funciona melhor para seus usuários e seu caso de negócios, completando assim as métricas técnicas e as métricas do mundo real.</p><h2>Recomendações finais para o uso de listas de julgamento</h2><p>Trabalhar com listas de julgamento não é apenas medir, mas também criar um framework que permita iterar com confiança. Para atingir isso, você pode seguir estas recomendações:</p><ol><li><strong>Comece pequeno, mas comece de algum lugar</strong>. Você não precisa ter 10.000 consultas com 50 listas de julgamento cada. Você só precisa identificar de 5 a 10 consultas mais importantes para seu case de negócios e definir quais documentos espera ver no topo dos resultados. Isso já te dá uma base. Normalmente, você quer começar com as principais consultas mais as que não obtiveram resultados. Você também pode começar a testar com uma métrica fácil de configurar, como Precision, e depois ir aumentando a complexidade.</li><li><strong>Validar com os usuários.</strong> Complemente os números com testes A/B em produção. Dessa forma, você saberá se mudanças que parecem boas nas métricas também estão gerando um impacto real.</li><li><strong>Mantenha a lista atualizada.</strong> Seu caso de negócio vai evoluir, assim como suas consultas importantes. Atualize seu julgamento periodicamente para refletir novas necessidades.</li><li><strong>Faça disso parte do fluxo.</strong> Integre listas de julgamento aos seus pipelines de desenvolvimento. Certifique-se de que cada alteração de configuração, sinônimo ou análise de texto seja automaticamente validada em relação à sua lista base.</li><li><strong>Conecte conhecimento técnico com estratégia.</strong> Não se limite a medir métricas técnicas como a precisão ou o recall. Use os resultados da sua avaliação para informar os resultados do negócio.</li></ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Crie um fluxo de trabalho de busca por IA financeira usando LangGraph.js e Elasticsearch]]></title>
            <description><![CDATA[Aprenda a usar o LangGraph.js com o Elasticsearch para criar um fluxo de trabalho de busca financeira com IA que converte consultas em linguagem natural em filtros dinâmicos e condicionais para análise de investimentos e do mercado.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A criação de aplicativos de busca com IA geralmente envolve a coordenação de múltiplas tarefas, recuperação e extração de dados em um fluxo de trabalho integrado. O LangGraph simplifica esse processo ao permitir que os desenvolvedores orquestrem agentes de IA usando uma estrutura baseada em nós. Neste artigo, vamos construir uma solução financeira usando <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph.js</a></p><h2>O que é LangGraph</h2><p><a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> é um framework para construir agentes de IA e orquestrá-los em um fluxo de trabalho para criar aplicações assistidas por IA. O LangGraph possui uma arquitetura de nós onde podemos declarar funções que representam tarefas e atribuí-las como nós do fluxo de trabalho. O resultado de múltiplos nós interagindo será um gráfico. O LangGraph faz parte do ecossistema mais amplo <a href="https://js.langchain.com/docs/introduction/">LangChain</a>, que oferece ferramentas para construir sistemas de IA modulares e componíveis.</p><p>Para entender melhor por que o LangGraph é útil, vamos resolver uma situação problemática usando-o.</p><h2>Visão geral da solução</h2><p>Em uma empresa de capital de risco, os investidores têm acesso a um grande banco de dados com muitas opções de filtragem, mas quando se deseja combinar critérios, o processo se torna difícil e lento. Isso pode fazer com que algumas startups relevantes não sejam encontradas para investimento. Isso resulta em gastar muitas horas tentando identificar os melhores candidatos, ou até mesmo em perder oportunidades.</p><p>Com o LangGraph e o Elasticsearch, podemos realizar buscar filtradas utilizando linguagem natural, eliminando a necessidade de os usuários construírem manualmente solicitações complexas com dezenas de filtros. Para torná-lo mais flexível, o fluxo de trabalho decide automaticamente com base na entrada do usuário entre dois tipos de consultas:</p><ul><li><strong>Consultas focadas em investimento</strong>: essas consultas visam aspectos financeiros e de financiamento de startups, como <a href="https://www.investopedia.com/articles/personal-finance/102015/series-b-c-funding-what-it-all-means-and-how-it-works.asp">rodadas de financiamento</a>, avaliação ou <a href="https://www.investopedia.com/terms/r/revenue.asp">receita</a>. <em>Exemplo:</em> &quot;Encontre startups com financiamento Série A ou Série B entre US$ 8 milhões e US$ 25 milhões e receita mensal acima de US$ 500 mil.&quot;</li><li><strong>Consultas focadas no mercado</strong>: essas consultas concentram-se em <a href="https://en.wikipedia.org/wiki/Vertical_market">verticais da indústria</a>, <a href="https://en.wikipedia.org/wiki/Target_market">mercados geográficos</a> ou <a href="https://www.investopedia.com/terms/b/businessmodel.asp">modelos de negócios</a>, ajudando a identificar oportunidades em setores ou regiões específicos. <em>Exemplo:</em> “Encontre startups de fintech e saúde em São Francisco, Nova York ou Boston.”</li></ul><p>Para manter a robustez das consultas, faremos com que o LLM crie <a href="https://www.elastic.co/docs/solutions/search/search-templates">modelos de busca</a> em vez de <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl">consultas DSL</a> completas. Assim, você sempre recebe a consulta que quer, e o LLM só precisa preencher as lacunas e não carregar a responsabilidade de construir a consulta que você precisa toda vez.</p><h2>O que você precisa para começar</h2><ul><li>APIKey do Elasticsearch</li><li>APIKey do OpenAPI</li><li>Node 18 ou mais recente</li></ul><h2>Instruções passo a passo</h2><p>Nesta seção, vamos ver como o app ficará. Para isso, usaremos o <a href="https://www.typescriptlang.org/">TypeScript</a>, um superconjunto do JavaScript que adiciona tipos estáticos para tornar o código mais confiável, fácil de manter e mais seguro, detectando erros precocemente e, ao mesmo tempo, permanecendo totalmente compatível com o JavaScript existente.</p><p>O fluxo dos nós terá a seguinte aparência:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" style="width: 100%;" /><p>A imagem acima é gerada pelo LangGraph e representa o fluxo de trabalho que define a ordem de execução e a lógica condicional entre nós:</p><ul><li><strong>decideStrategy: </strong>utiliza um LLM para analisar a consulta do usuário e decidir entre duas estratégias de busca especializadas: focada em investimento ou focada no mercado.</li><li><strong>prepareInvestSearch: </strong>extrai valores de filtro da consulta e constrói um modelo pré-definido enfatizando parâmetros financeiros e relacionados ao financiamento.</li><li><strong>prepareMarketSearch</strong>: também extrai valores de filtro, mas constrói parâmetros dinamicamente enfatizando o mercado, o setor e o contexto geográfico.</li><li><strong>executeSearch: </strong>envia a consulta construída para o Elasticsearch usando um modelo de busca e recupera os documentos correspondentes de inicialização.</li><li><strong>visualizeResults: </strong>formata os resultados finais em um resumo claro e legível que mostra atributos-chave da startup, como financiamento, setor e receita.</li></ul><p>Esse fluxo inclui uma <a href="https://langchain-ai.github.io/langgraphjs/how-tos/branching/?h=conditional#how-to-create-branches-for-parallel-node-execution">ramificação condicional</a>, funcionando como uma instrução “if”, que determina se deve usar o caminho de busca de investimentos ou de mercado com base na entrada do usuário. Essa lógica de decisão, conduzida pelo LLM, torna o fluxo de trabalho adaptável e sensível ao contexto, um mecanismo que exploraremos com mais detalhes nas próximas seções.</p><h3>Estado do LangGraph</h3><p>Antes de ver cada nó individualmente, precisamos entender como os nós se comunicam e compartilham dados. Para isso, o LangGraph nos permite definir o estado do fluxo de trabalho. Isso define o estado compartilhado que será passado entre os nós.</p><p>O estado funciona como um container compartilhado que armazena dados intermediários ao longo do fluxo de trabalho: começa com a consulta em linguagem natural do usuário, depois mantém a estratégia de busca selecionada, os parâmetros preparados para o Elasticsearch, os resultados de busca recuperados e, finalmente, a saída formatada.</p><p>Essa estrutura permite que cada nó leia e atualize o estado, garantindo um fluxo consistente de informações desde a entrada do usuário até a visualização final.</p><pre><code>const VCState = Annotation.Root({
  input: Annotation<string>(), // User's natural language query
  searchStrategy: Annotation<string>(), // Search strategy chosen by LLM
  searchParams: Annotation<any>(), // Prepared search parameters
  results: Annotation<any[]>(), // Search results
  final: Annotation<string>(), // Final formatted response
});</code></pre><h3>Configure o aplicativo</h3><p>Todo o código desta seção pode ser encontrado no <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch">repositório elasticsearch-labs</a>.</p><p>Abra um terminal na pasta em que o app estará localizado e inicialize um app Node.js com o comando:</p><pre><code>npm init -y</code></pre><p>Agora podemos instalar as dependências necessárias para este projeto:</p><pre><code>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod && npm install --save-dev @types/node tsx typescript</code></pre><ul><li><strong><code>@elastic/elasticsearch</code></strong>: Nos ajuda a lidar com requisições do Elasticsearch, como ingestão e recuperação de dados.</li><li><strong><code>@langchain/langgraph</code></strong>: dependência de JS para fornecer todas as ferramentas LangGraph.</li><li><strong><code>@langchain/openai</code></strong>Cliente OpenAI LLM para LangChain.</li><li>@langchain/núcleo: fornece os blocos de construção fundamentais para apps LangChain, incluindo modelos de prompt.</li><li><strong><code>dotenv</code></strong>: Dependência necessária para usar variáveis de ambiente em JavaScript.</li><li><strong><code>zod</code></strong>: Dependência para digitar dados.</li></ul><p><code>@types/node</code> <code>tsx</code> <code>typescript</code> nos permite escrever e executar o código TypeScript.</p><p>Agora, crie os seguintes arquivos:</p><ul><li><code>elasticsearchSetup<a href="http://ingest.ts/">.ts</a></code>: Criará os mapeamentos de índice, carregará o conjunto de dados de um arquivo JSON e fará a ingestão dos dados no Elasticsearch.</li><li><a href="http://main.ts/"><code>main.ts</code></a>: incluirá o aplicativo LangGraph.</li><li><code>.env</code>: arquivo para armazenar as variáveis de ambiente</li></ul><p>No arquivo <code>.env</code>, vamos adicionar as seguintes variáveis de ambiente:</p><pre><code>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"</code></pre><p>O APIKey da OpenAPI não será usado diretamente no código; em vez disso, será usado internamente pela biblioteca <code>@langchain/openai</code>.</p><p>Toda a lógica relacionada à criação de mapeamentos, modelos de busca e ingestão de conjuntos de dados pode ser encontrada no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>. Nos próximos passos, vamos focar no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/main.ts"><code>main.ts</code></a> . Além disso, você pode verificar o conjunto de dados para entender melhor como os dados aparecem no <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/dataset.json"><code>dataset.json</code></a>.</p><h3>Aplicativo LangGraph</h3><p>No arquivo <code>main.ts</code>, vamos importar algumas dependências necessárias para consolidar a aplicação LangGraph. Neste arquivo, você também deve incluir as funções de nós e a declaração de estado. A declaração do gráfico será feita em um método <code>main</code> nos próximos passos. O arquivo <code>elasticsearchSetup.ts</code> conterá ajudantes Elasticsearch que vamos usar dentro dos nós em etapas futuras.</p><pre><code>import { writeFileSync } from "node:fs";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
import {
  esClient,
  ingestDocuments,
  createSearchTemplates,
  INDEX_NAME,
  INVESTMENT_FOCUSED_TEMPLATE,
  MARKET_FOCUSED_TEMPLATE,
  createIndex,
} from "./elasticsearchSetup.js";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });</code></pre><p>Como mencionado anteriormente, o cliente LLM será usado para gerar os parâmetros de busca do Elasticsearch com base na pergunta do usuário.</p><pre><code>async function saveGraphImage(app: any): Promise<void> {
  try {
    const drawableGraph = app.getGraph();
    const image = await drawableGraph.drawMermaidPng();
    const arrayBuffer = await image.arrayBuffer();

    const filePath = "./workflow_graph.png";
    writeFileSync(filePath, new Uint8Array(arrayBuffer));
    console.log(`📊 Workflow graph saved as: ${filePath}`);
  } catch (error: any) {
    console.log("⚠️  Could not save graph image:", error.message);
  }
}</code></pre><p>O método acima gera a imagem do gráfico em formato PNG e usa a <a href="https://mermaid.ink/">API Mermaid.INK</a> nos bastidores. Isso é útil se você quiser ver como os nós do app interagem entre si com uma visualização estilizada.</p><h3>Nós do LangGraph</h3><p>Agora vamos analisar cada nó em detalhes:</p><h3>nó decideSearchStrategy</h3><p>O node <code>decideSearchStrategy</code> analisa a entrada do usuário e determina se realiza uma buscar focada em investimento ou no mercado. Ele utiliza um LLM com um esquema de saída estruturado (definido com Zod) para classificar o tipo de consulta. Antes de tomar a decisão, o sistema recupera os filtros disponíveis do índice por meio de uma agregação, garantindo que o modelo tenha um contexto atualizado sobre setores, locais e dados de financiamento.</p><p>Para extrair os valores possíveis dos filtros e enviá-los ao LLM, vamos usar uma consulta de <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agregação</a> para recuperá-los diretamente do índice do Elasticsearch. Essa lógica é alocada em um método chamado <code>getAvailableFilters</code>:</p><pre><code>async function getAvailableFilters() {
  try {
    const response = await esClient.search({
      index: INDEX_NAME,
      size: 0,
      aggs: {
        industries: {
          terms: { field: "industry", size: 100 },
        },
        locations: {
          terms: { field: "location", size: 100 },
        },
        funding_stages: {
          terms: { field: "funding_stage", size: 20 },
        },
        business_models: {
          terms: { field: "business_model", size: 10 },
        },
        lead_investors: {
          terms: { field: "lead_investor", size: 100 },
        },
        funding_amount_stats: {
          stats: { field: "funding_amount" },
        },
      },
    });

    return response.aggregations;
  } catch (error) {
    console.error("❌ Error getting available filters:", error);
    return {};
  }
}</code></pre><p>Com a consulta de agregação acima, temos os seguintes resultados:</p><pre><code>{
  "industries": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "logistics",
        "doc_count": 5
      },
      ...
    ]
  },
  "locations": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "San Francisco, CA",
        "doc_count": 4
      },
      {
        "key": "New York, NY",
        "doc_count": 3
      },
      ...
    ]
  },
  "funding_stages": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Series A",
        "doc_count": 8
      },
      ...
    ]
  },
  "business_models": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "B2B",
        "doc_count": 13
      },
      ...
    ]
  },
  "lead_investors": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Battery Ventures",
        "doc_count": 1
      },
      {
        "key": "Benchmark Capital",
        "doc_count": 1
      },
      ...
    ]
  },
  "funding_amount_stats": {
    "count": 20,
    "min": 4500000,
    "max": 35000000,
    "avg": 14075000,
    "sum": 281500000
  }
}</code></pre><p>Veja todos os resultados <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/responses/aggregationsResponse.json">aqui</a>.</p><p>Para ambas as estratégias, usaremos busca híbrida para detectar tanto a parte estruturada da pergunta (filtros) quanto as partes mais subjetivas (semântica). Aqui está um exemplo de ambas as consultas usando <a href="https://www.elastic.co/docs/solutions/search/search-templates">templates de busca</a>:</p><pre><code>await esClient.putScript({
      id: INVESTMENT_FOCUSED_TEMPLATE,
      script: {
        lang: "mustache",
        source: `{
          "size": 5,
          "retriever": {
            "rrf": {
              "retrievers": [
                {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "semantic_field",
                        "query": "{{query_text}}"
                      }
                    }
                  }
                },
                {
                  "standard": {
                    "query": {
                      "bool": {
                        "filter": [
                          {"terms": {"funding_stage": {{#join}}{{#toJson}}funding_stage{{/toJson}}{{/join}}}},
                          {"range": {"funding_amount": {"gte": {{funding_amount_gte}}{{#funding_amount_lte}},"lte": {{funding_amount_lte}}{{/funding_amount_lte}}}}},
                          {"terms": {"lead_investor": {{#join}}{{#toJson}}lead_investor{{/toJson}}{{/join}}}},
                          {"range": {"monthly_revenue": {"gte": {{monthly_revenue_gte}}{{#monthly_revenue_lte}},"lte": {{monthly_revenue_lte}}{{/monthly_revenue_lte}}}}}
                        ]
                      }
                    }
                  }
                }
              ],
              "rank_window_size": 100,
              "rank_constant": 20
            }
          }
        }`,
      },
    });</code></pre><p>Veja as consultas detalhadas no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts#L119"><code>elasticsearchSetup.ts</code></a> . No nó a seguir, será decidido qual das duas consultas será usada:</p><pre><code>// Node 1: Decide search strategy using LLM
async function decideSearchStrategy(state: typeof VCState.State) {
  // Zod schema for specialized search strategy decision
  const SearchDecisionSchema = z.object({
    search_type: z
      .enum(["investment_focused", "market_focused"])
      .describe("Type of specialized search strategy to use"),
    reasoning: z
      .string()
      .describe("Brief explanation of why this search strategy was chosen"),
  });

  const decisionLLM = llm.withStructuredOutput(SearchDecisionSchema);

  // Get dynamic filters from Elasticsearch
  const availableFilters = await getAvailableFilters();

  const prompt = `Query: "${state.input}"
    Available filters: ${JSON.stringify(availableFilters, null, 2)}

    Choose between two specialized search strategies:
    
    - investment_focused: For queries about funding stages, funding amounts, monthly revenue, lead investors, financial performance
    
    - market_focused: For queries about industries, locations, business models, market segments, geographic markets
    
    Analyze the query intent and choose the most appropriate strategy.
  `;

  try {
    const result = await decisionLLM.invoke(prompt);
    console.log(
      `🤔 Search strategy: ${result.search_type} - ${result.reasoning}`
    );

    return {
      searchStrategy: result.search_type,
    };
  } catch (error: any) {
    console.error("❌ Error in decideSearchStrategy:", error.message);
    return {
      searchStrategy: "investment_focused",
    };
  }
}</code></pre><h3>nós prepareInvestmentSearch e prepareMarketSearch</h3><p>Ambos os nós usam uma função auxiliar compartilhada, <code>extractFilterValues</code>, que utiliza o LLM para identificar filtros relevantes mencionados na entrada do usuário, como setor, localização, estágio de financiamento, modelo de negócios, etc. Estamos usando este esquema para construir nosso <a href="https://www.elastic.co/docs/solutions/search/search-templates">modelo de busca</a>.</p><pre><code>// Extract all possible filter values from user input
async function extractFilterValues(input: string) {
  const FilterValuesSchema = z.object({
    // Investment-focused filters
    funding_stage: z
      .array(z.string())
      .default([])
      .describe("Funding stage values mentioned in query"),
    funding_amount_gte: z
      .number()
      .default(0)
      .describe("Minimum funding amount in USD"),
    funding_amount_lte: z
      .number()
      .default(100000000)
      .describe("Maximum funding amount in USD"),
    lead_investor: z
      .array(z.string())
      .default([])
      .describe("Lead investor values mentioned in query"),
    monthly_revenue_gte: z
      .number()
      .default(0)
      .describe("Minimum monthly revenue in USD"),
    monthly_revenue_lte: z
      .number()
      .default(10000000)
      .describe("Maximum monthly revenue in USD"),
    industry: z
      .array(z.string())
      .default([])
      .describe("Industry values mentioned in query"),
    location: z
      .array(z.string())
      .default([])
      .describe("Location values mentioned in query"),
    business_model: z
      .array(z.string())
      .default([])
      .describe("Business model values mentioned in query"),
  });

  const extractorLLM = llm.withStructuredOutput(FilterValuesSchema);
  const availableFilters = await getAvailableFilters();

  const extractPrompt = `Extract ALL relevant filter values from: "${input}"
    Available options: ${JSON.stringify(availableFilters, null, 2)}
    Extract only values explicitly mentioned in the query. Leave fields empty if not mentioned.`;

  return await extractorLLM.invoke(extractPrompt);
}</code></pre><p>Dependendo da intenção detectada, o fluxo de trabalho seleciona um de dois caminhos:</p><p><strong>prepareInvestmentSearch:</strong> desenvolve parâmetros de busca orientados financeiramente, incluindo estágio de financiamento, valor do investimento, investidor e informações de renovação. Você pode encontrar o modelo completo de consulta no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>:</p><pre><code>// Node 2A: Prepare Investment-Focused Search Parameters 
async function prepareInvestmentSearch(state: typeof VCState.State) {
  console.log(
    "💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: INVESTMENT_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing investment-focused params:", error);
    return {
      searchParams: {},
    };
  }
}</code></pre><p><strong>prepareMarketSearch:</strong> cria parâmetros orientados pelo mercado, focados em setores, geografias e modelos de negócios. Veja a consulta completa no arquivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a> :</p><pre><code>// Node 2B: Prepare Market-Focused Search Parameters
async function prepareMarketSearch(state: typeof VCState.State) {
  console.log(
    "🔍 Preparing MARKET-FOCUSED search parameters with market emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: MARKET_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing market-focused params:", error);
    return {};
  }
}</code></pre><h3>nó executeSearch</h3><p>Este nó pega os parâmetros de busca gerados do estado e os envia primeiro para o Elasticsearch, usando a <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-render-search-template">API _render</a> para visualizar a consulta para fins de depuração, e então envia uma solicitação para buscar os resultados.</p><pre><code>// Node 3: Execute Search
async function executeSearch(state: typeof VCState.State) {
  const { searchParams } = state;

  try {
    // getting formed query from template for debugging
    const renderedTemplate = await esClient.renderSearchTemplate({
      id: searchParams.template_id,
      params: searchParams,
    });

    console.log(
      "📋 Complete query:",
      JSON.stringify(renderedTemplate.template_output, null, 2)
    );

    const results = await esClient.searchTemplate({
      index: INDEX_NAME,
      id: searchParams.template_id,
      params: searchParams,
    });

    return {
      results: results.hits.hits.map((hit: any) => hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}</code></pre><h3>nó visualizeResults</h3><p>Por fim, este nó exibe os resultados do Elasticsearch.</p><pre><code>// Node 4: Visualize results
async function visualizeResults(state: typeof VCState.State) {
  const results = state.results || [];

  let formattedResults = `🎯 Found ${results.length} startups matching your criteria:\n\n`;

  results.forEach((startup: any, index: number) => {
    formattedResults += `${index + 1}. **${startup.company_name}**\n`;
    formattedResults += `   📍 ${startup.location} | 🏢 ${startup.industry} | 💼 ${startup.business_model}\n`;
    formattedResults += `   💰 ${startup.funding_stage} - $${(
      startup.funding_amount / 1000000
    ).toFixed(1)}M\n`;
    formattedResults += `   👥 ${startup.employee_count} employees | 📈 $${(
      startup.monthly_revenue / 1000
    ).toFixed(0)}K MRR\n`;
    formattedResults += `   🏦 Lead: ${startup.lead_investor}\n`;
    formattedResults += `   📝 ${startup.description}\n\n`;
  });

  return {
    final: formattedResults,
  };
}</code></pre><p>Programaticamente, o gráfico completo tem a seguinte aparência:</p><pre><code>  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) => state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -> RRF template preparation
        market_focused: "prepareMarket", // If market focused -> dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -> execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -> execute
    .addEdge("executeSearch", "visualizeResults") // Execute -> visualize
    .addEdge("visualizeResults", END); // End workflow</code></pre><p>Como você pode ver, temos uma aresta condicional onde o app decide qual &quot;caminho&quot; ou nó será executado em seguida. Esse recurso é útil quando fluxos de trabalho precisam de lógica de ramificação, como escolher entre várias ferramentas ou incluir uma etapa com uma pessoa no ciclo.</p><p>Com os recursos do núcleo do LangGraph entendidos, podemos configurar o aplicativo onde o código será executado:</p><p>Junte tudo em um método <code>main</code>; aqui declaramos o gráfico com todos os elementos sob a variável fluxo de trabalho:</p><pre><code>async function main() {
  await createIndex();
  await createSearchTemplates();
  await ingestDocuments();

  // Create the workflow graph with shared state
  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) => state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -> RRF template preparation
        market_focused: "prepareMarket", // If market focused -> dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -> execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -> execute
    .addEdge("executeSearch", "visualizeResults") // Execute -> visualize
    .addEdge("visualizeResults", END); // End workflow


  const app = workflow.compile();

  await saveGraphImage(app);

  const query =
    "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K";

  const marketResult = await app.invoke({ input: query });
  console.log(marketResult.final);
}</code></pre><p>A variável de consulta simula a entrada do usuário inserida em uma barra de busca hipotética:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" style="width: 100%;" /><p>A partir da frase em linguagem natural &quot;Encontre startups com financiamento Série A ou Série B entre US$ 8M–US$ 25M e receita mensal acima de US$ 500K&quot;, todos os filtros serão extraídos.</p><p>Finalmente, invoque o método principal:</p><pre><code>main().catch(console.error);</code></pre><h3>Resultados</h3><pre><code>🔍 Checking if index exists...
🏗️ Creating index...
✅ Index created successfully!
Ingesting documents...
✅ Documents ingested successfully!
✅ Investment-focused template created successfully!
✅ Market-focused template created successfully!

📊 Workflow graph saved as: ./workflow_graph.png

🔍 Query: "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"

🤔 Search strategy: investment_focused - The query specifically seeks profitable fintech startups with defined funding amounts and high monthly revenue, which aligns closely with financial performance metrics and investment-related criteria.

💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "funding_stage": [
                        "Series A",
                        "Series B"
                      ]
                    }
                  },
                  {
                    "range": {
                      "funding_amount": {
                        "gte": 8000000,
                        "lte": 25000000
                      }
                    }
                  },
                  {
                    "terms": {
                      "lead_investor": []
                    }
                  },
                  {
                    "range": {
                      "monthly_revenue": {
                        "gte": 500000,
                        "lte": 0
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 100,
      "rank_constant": 20
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **TechFlow**
   📍 San Francisco, CA | 🏢 logistics | 💼 B2B
   💰 Series A - $8.0M
   👥 45 employees | 📈 $500K MRR
   🏦 Lead: Sequoia Capital
   📝 TechFlow optimizes supply chain operations using AI-powered route optimization and real-time tracking. Founded in 2023, shows remarkable growth with $500K monthly revenue.

2. **DataViz**
   📍 New York, NY | 🏢 enterprise software | 💼 B2B
   💰 Series A - $10.0M
   👥 42 employees | 📈 $450K MRR
   🏦 Lead: Battery Ventures
   📝 DataViz creates intuitive data visualization tools for enterprise customers. No-code platform allows business users to create dashboards without technical expertise.

3. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

4. **UrbanMobility**
   📍 New York, NY | 🏢 logistics | 💼 B2B2C
   💰 Series B - $15.0M
   👥 78 employees | 📈 $750K MRR
   🏦 Lead: Kleiner Perkins
   📝 UrbanMobility revolutionizes urban transportation through autonomous delivery drones and smart logistics hubs. Partners with major retailers for same-day delivery across Manhattan and Brooklyn.

5. **HealthTech Solutions**
   📍 Boston, MA | 🏢 healthcare | 💼 B2B
   💰 Series B - $18.0M
   👥 95 employees | 📈 $900K MRR
   🏦 Lead: General Catalyst
   📝 HealthTech Solutions develops medical devices and software for remote patient monitoring. Comprehensive telehealth platform reducing hospital readmissions by 30%.

✨  Done in 18.80s.</code></pre><p>Para a entrada enviada, a aplicação escolhe o caminho <strong>focado no investimento</strong> e, como resultado, podemos ver a consulta Elasticsearch gerada pelo fluxo de trabalho LangGraph, que extrai os valores e intervalos a partir da entrada do usuário. Também podemos ver a consulta enviada para o Elasticsearch com os valores extraídos aplicados e, finalmente, os resultados formatados pelo node <code>visualizeResults</code> com os resultados.</p><p>Agora vamos testar o nó <strong>focado no mercado</strong> usando a consulta &quot;Encontre startups de fintech e saúde em São Francisco, Nova York ou Boston&quot;:</p><pre><code>...

🔍 Query: Find fintech and healthcare startups in San Francisco, New York, or Boston

🤔 Search strategy: market_focused - The query is focused on finding fintech startups in San Francisco that are disrupting traditional banking and payment systems, which pertains to specific industries (fintech) and locations (San Francisco). Thus, a market-focused strategy is more appropriate.

🔍 Preparing MARKET-FOCUSED search parameters with market emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find fintech and healthcare startups in San Francisco, New York, or Boston"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "industry": [
                        "fintech",
                        "healthcare"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "location": [
                        "San Francisco, CA",
                        "New York, NY",
                        "Boston, MA"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "business_model": []
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 50,
      "rank_constant": 10
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

2. **CryptoWallet**
   📍 Miami, FL | 🏢 fintech | 💼 B2C
   💰 Series B - $16.0M
   👥 73 employees | 📈 $820K MRR
   🏦 Lead: Coinbase Ventures
   📝 CryptoWallet provides secure digital wallet solutions for cryptocurrency trading and storage. Multi-chain support with enterprise-grade security features.

...

✨  Done in 7.41s.</code></pre><h2>Aprendizados</h2><p>Durante o processo de escrita, aprendi:</p><ul><li>Devemos mostrar ao LLM os valores exatos dos filtros; caso contrário, dependemos de o usuário digitar os valores exatos das coisas. Para baixa cardinalidade, essa abordagem é válida; mas, quando a cardinalidade é alta, precisamos de algum mecanismo para filtrar os resultados.</li><li>Usar templates para busca torna os resultados muito mais consistentes do que deixar o LLM escrever a consulta Elasticsearch, e também é mais rápido</li><li>Arestas condicionais são um mecanismo poderoso para construir aplicações com múltiplas variantes e caminhos ramificados.</li><li>A saída estruturada é extremamente útil ao gerar informações com LLMs porque impõe respostas previsíveis e seguras para tipos. Isso melhora a confiabilidade e reduz as interpretações errôneas imediatas.</li></ul><p>Combinar busca semântica e estruturada por meio da recuperação híbrida produz resultados melhores e mais relevantes, equilibrando precisão e compreensão do contexto.</p><h2>Conclusão</h2><p>Neste exemplo, combinamos LangGraph.js com o Elasticsearch para criar um fluxo de trabalho dinâmico capaz de interpretar consultas em linguagem natural e decidir entre estratégias de busca voltadas para finanças ou para o mercado. Essa abordagem reduz a complexidade de elaborar consultas manuais, ao mesmo tempo em que melhora a flexibilidade e a precisão para analistas de capital de risco.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Melhorando a interatividade do dashboard do Kibana com controles de variáveis]]></title>
            <description><![CDATA[Descubra como usar controles de variáveis no Kibana 8.18+ para filtrar visualizações específicas, ajustar intervalos e agrupar por diferentes campos nos dashboards do Kibana.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/kibana-dashboard-interactivity-variable-controls-overview</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/kibana-dashboard-interactivity-variable-controls-overview</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Teresa Alvarez Soler]]></dc:creator>
            <pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/59aa3adffc8c759e42b961ef7d63719ce232893a-1348x830.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Temos o prazer de anunciar que <strong>os controles de variáveis já estão disponíveis no dashboard do Kibana</strong> a partir da versão 8.18 e em toda a série 9.x! Este recurso tem sido uma das adições mais solicitadas pelos usuários do dashboard — e finalmente chegou 🎉 Nos últimos meses, continuamos expandindo e aprimorando <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls#add-variable-control">os controles de variáveis</a>, tornando este o momento perfeito para dedicarmos um post do blog inteiro a eles.</p><h2>O que são controles de variáveis?</h2><p>Se você já usou dashboards do Kibana, provavelmente conhece nossos controles clássicos de dashboards: aqueles menus suspensos úteis que mostram os valores dos seus dados para que você possa filtrar informações com alguns cliques.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b82c1b25a0b38661e5ce4552f763be487d5074aa-1600x701.png" style="width: 100%;" /><p>Os controles de variáveis parecem semelhantes à primeira vista, mas têm um diferencial inteligente: em vez de filtrar automaticamente todos os painéis do seu dashboard, eles podem ser inseridos diretamente em <a href="https://www.elastic.co/docs/explore-analyze/visualize/esorql">consultas ES|QL dentro de visualizações específicas</a>.</p><p>Isso significa que <em>você</em> pode decidir onde cada controle se aplica. Melhor ainda, você pode usá-los para todos os tipos de truques criativos, como ajustar intervalos, alternar campos de detalhamento ou alterar parâmetros de visualização em tempo real. Basicamente, eles proporcionam aos dashboards uma experiência verdadeiramente interativa, permitindo que você obtenha insights com mais rapidez e facilidade.</p><h2>Casos de uso para controles de variáveis</h2><p>Certo, os controles variáveis parecem úteis, mas o que você pode realmente fazer com eles? Aqui estão alguns exemplos de como eles elevam o nível de seu dashboard:</p><h3>Filtrar visualizações selecionadas</h3><p>Deseja filtrar <em>algumas</em> visualizações, mas não mexer em outras? Os controles de variáveis permitem exatamente isso. Escolha os painéis aos quais deseja responder e conecte-os nas consultas ES|QL por trás das suas visualizações.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/efa367363830b03bc67028aceafe78c4b44e578f-1440x562.gif" style="width: 100%;" /><h3>Selecionar diferentes intervalos</h3><p>Permita que seus usuários escolham entre &quot;5 minutos&quot;, &quot;1 hora&quot;, &quot;1 dia&quot; ou quaisquer buckets que façam sentido. Crie um controle de variáveis com intervalos predefinidos e conecte-o à sua consulta de séries temporais.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/62aa9f4e728036f8c70213b76b1cf131f36f5b4d-1440x606.gif" style="width: 100%;" /><h3>Funções de alteração</h3><p>Em vez de criar vários gráficos para cada operação, permita que os usuários do dashboard escolham se desejam ver o máximo, a média, diferentes percentis ou qualquer outro agregador.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f6a2b4c73dc35efe462c2924a153d7b3fa3a7922-1436x606.gif" style="width: 100%;" /><h3>Agrupe por diferentes campos</h3><p>Às vezes, é necessário dividir os dados conforme diferentes dimensões durante uma investigação. Com controles de variáveis, você pode definir múltiplos campos &quot;agrupar por&quot; e permitir que os usuários do dashboard escolham aquele que os ajude a descobrir seus insights.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fe8745a6fddccadba0666686b8ebc67fdaf64158-1438x606.gif" style="width: 100%;" /><h2>Como você pode criá-los?</h2><p>A maneira mais fácil (e provavelmente mais agradável) de criar um controle de variável é diretamente pelo <strong>editor de consultas ES|QL</strong> na sua visualização. Basta começar a digitar sua consulta, usar o menu de preenchimento automático, e o Kibana vai ajudar a estruturar o controle para você.</p><p>Mas, se preferir começar pela própria variável, você também pode ir para: <strong>Adicionar painel → Controles → Controle de variável</strong> e adicionar a variável às suas visualizações após criar o controle.</p><h3>Exemplo 1: Controle de filtragem com seleção de múltiplos valores</h3><p>1. Escolha uma visualização atrelada a uma consulta ES|QL e clique em &quot;Criar controle&quot; dentro da instrução WHERE</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46cb6f2a6775aee152d42eb5ee85170f1bdf26cb-1600x668.png" style="width: 100%;" /><p>2. Você será redirecionado automaticamente para o submenu de criação de variáveis, onde o tipo &quot;Valores de uma consulta&quot; será selecionado para você e o nome da variável já estará pré-preenchido. Lembre-se de que o nome de um controle sempre precisa começar com &quot;?...&quot;. para ser usado na consulta de visualização.</p><p>Normalmente, você precisará de uma consulta como esta para obter os valores de um campo e atualizá-los de acordo com o intervalo de tempo selecionado no dashboard:</p><pre><code>FROM <datasource_name>
| WHERE @timestamp <=?_tend and @timestamp >?_tstart
| STATS BY <field_name></code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a2a72d4e3159923c6207908da9b4172e27cd5f81-1600x716.png" style="width: 100%;" /><p>3. Ao salvar o controle, ele será exibido na parte superior do painel, e sua consulta de visualização será atualizada com o nome do controle da variável.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5fc434c8951889e9769652b675191711d126a685-1600x653.png" style="width: 100%;" /><p>4. Se você quiser adicionar <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls#esql-multi-values-controls">seleção multi-valor</a> ao controle, precisa usar a função <code>MV_CONTAINS</code> na consulta e selecionar &quot;Permitir múltiplas seleções&quot; durante a criação do controle na etapa 2 (disponível a partir da 9.3).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1f237cea0a37cb25a7917a2a683707a269adae8e-1600x670.png" style="width: 100%;" /><h3>Exemplo 2: Controle de intervalo de tempo</h3><p>Se estiver montando uma série temporal, você poderá facilmente adicionar um controle de variável para o intervalo do histograma de data:</p><p>1. Ao escrever uma consulta ES|QL para sua série temporal, clique em &quot;Criar controle&quot;. Ao criar uma variável para intervalos, é melhor usar <code>TBUCKET</code> em vez de <code>BUCKET</code> para que aceite intervalos mais legíveis como &quot;1 hora&quot;, &quot;1 dia&quot; etc. Também haverá uma opção automática para <code>TBUCKET</code> em breve, para que possa se adaptar automaticamente aos intervalos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b0ad53d790ff9bdd42db5e77477318319f423534-1600x664.png" style="width: 100%;" /><p>2. Defina os intervalos para preencher as opções no menu suspenso.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f3bd83f530cfa4698c1a3b1ae60d08d0414043b5-1600x757.png" style="width: 100%;" /><p>3. Selecione diferentes intervalos no menu suspenso e veja como sua visualização muda.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f928d9c70929f64926e065059188d140cd48943-1600x671.png" style="width: 100%;" /><h3>Exemplo 3: variáveis para funções</h3><ol><li>Crie uma variável usando o tipo de controle &quot;Valores estáticos&quot; e adicione nomes de funções aos seus valores suspensos. É importante usar um nome para sua variável que comece com “??...” para substituir funções.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/531444237b7e152d3c8a6f3ca7e464f954f9e856-1600x663.png" style="width: 100%;" /><p>2. Inclua o nome da variável na sua consulta ES|QL.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9858442abb26d8d266d464852871b139fde63b89-1600x665.png" style="width: 100%;" /><h3>Exemplo 4: variáveis para campos</h3><ol><li>Você pode usar o tipo de controle &quot;Valores estáticos&quot; e anotar os nomes dos campos que quiser. É importante usar um nome de variável que comece com &quot;??...&quot; para aplicá-lo aos campos.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33534c3df2fae024b25c28b4aed5d742e54202a2-1600x710.png" style="width: 100%;" /><p>2. Faça referência à variável onde quiser na consulta de visualização.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/71cdf3e9df72c59d957628a3aa6e4aa9bd60d6d5-1600x676.png" style="width: 100%;" /><h2>Controles de variáveis no Discover</h2><p>Controles variáveis não são apenas um recurso do dashboard — eles também estão disponíveis diretamente no editor ES|QL no Discover. Você pode construir controles para uma experiência de exploração de dados mais rápida no Discover, trazê-los para o dashboard e vice-versa.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fdddeec902d0bc746caed9276d01d7d48793dd85-1600x709.png" style="width: 100%;" /><h2>Detalhes técnicos</h2><p>A esta altura, você provavelmente já percebeu que os controles de variáveis vêm com algumas regras — como quais partes de uma consulta eles podem referenciar e os prefixos de nomeação que você precisa usar (&quot;?...&quot; para valores e &quot;?? ...” para campos ou funções). O motivo disso é que variáveis não são apenas simples substituições de string no cliente. Elas são, na verdade, cidadãos de primeira classe na própria linguagem de consulta (conhecidos como <a href="https://www.elastic.co/docs/solutions/search/agent-builder/tools/esql-tools#parameter-types">parâmetros no ES|QL</a>).</p><p></p><p>Este design traz grandes vantagens. Por exemplo, o Kibana consegue entender o contexto de cada variável, o que nos permite gerar e preencher automaticamente sua configuração para você. Também é muito mais seguro: como a linguagem valida rigorosamente entradas variáveis, ela impede injeções nocivas e erros se algo parece errado. Além disso, melhora o desempenho e a estabilidade ao transferir validações complexas e tratamento de erros para o servidor em vez do cliente. Uma observação sobre desempenho: uma prática recomendada é criar variáveis que incluam consultas rápidas, pois elas são carregadas antes do dashboard, portanto, consultas lentas podem afetar todo o desempenho do dashboard.</p><p>É claro, essa arquitetura também vem com algumas <a href="https://www.elastic.co/docs/solutions/search/agent-builder/limitations-known-issues#esql-limitations">limitações</a>—por enquanto. Variáveis ainda não permitem uma opção “Qualquer” para filtragem, e elas não podem ser usadas atualmente com certos operadores como <code>LIKE</code><strong> </strong>ou <code>FROM</code> (para alternar fontes de dados). A boa notícia? Estamos trabalhando ativamente para adicionar essas funcionalidades.</p><h2>O que o futuro reserva para os controles</h2><p>Não vamos parar aqui! Algumas das melhorias no nosso radar incluem:</p><p>✨ A capacidade de posicionar controles em qualquer lugar do painel</p><p>✨ Encadeando seus controles — ou seja, a saída de um controle se torna a entrada do próximo</p><p>✨ Melhores opções de seleção como seleção &quot;Qualquer&quot; para variáveis</p><p>✨ Novos tipos de controle (controle de buscar e variáveis para suas fontes de dados)</p><p>✨ E mais melhorias de qualidade de vida que vocês pediram, como pré-filtrar controles normais.</p><p>Se você tiver ideias ou feedback, adoraríamos saber.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-gpu-accelerated-vector-indexing-nvidia</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Chris Hegarty, Hemant Malik, Corey Nolet, Manas Singh, Mithun Radhakrishnan, Mayya Sharipova, Lorenzo Dematte, Ben Frederickson]]></dc:creator>
            <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/08f7469a4daaf67b7c5999585aae179b6680c78d-896x746.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5f6daa1eb07f704a6707d9e6b7ccb81d0abaa8c9-566x419.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3ea255bd9bfd7983f78143c5eba999d2149d72be-671x356.png" style="width: 100%;" /><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><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.</li><li><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.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f60ea9ee5996e456f393ffd195ee7eada6e5a7c2-948x387.png" style="width: 100%;" /><ul><li><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.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5e680a5fc41700a877f3d8b2e5ce18ebd3f37a0b-1600x562.png" style="width: 100%;" /><ul><li><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.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/48a2b7758606bd321712b7d8378cd2640e652a4e-1384x544.png" style="width: 100%;" /><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&nbsp; 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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8ffa58cae3ad175ef2932a351aeef4c34a1407b9-948x394.png" style="width: 100%;" /><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>
        </item>
        <item>
            <title><![CDATA[ES|QL na 9.2: Smart Lookup Joins e compatibilidade com séries temporais]]></title>
            <description><![CDATA[Explore três atualizações separadas do ES|QL no Elasticsearch 9.2: um LOOKUP JOIN aprimorado para uma correlação de dados mais expressiva, o novo comando TS para análise de séries temporais e o comando INLINE STATS flexível para agregação.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/esql-elasticsearch-9-2-multi-field-joins-ts-command</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tyler Perkins, Kostas Krikellas, Julian Kiryakov]]></dc:creator>
            <pubDate>Tue, 02 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/415894e21e7758c907d6e60d4efc94230349beef-2012x1164.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Elasticsearch 9.2, lançado em outubro, está repleto de avanços significativos que tornam a análise dos seus dados mais rápida, mais flexível e mais acessível do que nunca. No centro desta versão estão importantes melhorias no ES|QL, nossa linguagem de consulta baseada em pipes, projetada para oferecer ainda mais valor diretamente aos usuários finais.</p><p>Aqui está uma visão dos recursos do Elasticsearch 9.2 que transformarão seus fluxos de trabalho de análise de dados com o ES|QL.</p><h2>Revolucionando a correlação de dados: um Lookup Join mais inteligente, rápido e flexível</h2><p>O comando <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/lookup-join">LOOKUP JOIN</a> no ES|QL passou por uma transformação significativa no Elasticsearch 9.2, tornando-se dramaticamente mais eficiente e versátil. O comando LOOKUP JOIN combina dados da sua tabela de resultados da consulta ES|QL com registros correspondentes de um índice de modo de consulta especificado. Ele adiciona campos do índice de pesquisa como novas colunas à sua tabela de resultados com base em valores correspondentes no campo de join. Anteriormente, a junção de dados estava limitada a um único campo e igualdade simples. Não mais! Essas melhorias capacitam você a lidar com cenários complexos de correlação de dados de forma fácil.</p><p><strong>Os principais aprimoramentos do Lookup Join incluem:</strong></p><ul><li><strong>Joins de múltiplos campos:</strong> crie joins facilmente em múltiplos campos. Por exemplo, para unir <code>application_logs</code> com <code>service_registry</code> em <code>service_name</code>, <code>environment</code> e <code>version:</code></li></ul><pre><code>FROM application_logs
| LOOKUP JOIN service_registry ON service_name, environment, version</code></pre><ul><li><strong>Liberando predicados de joins complexos com expressões (prévia técnica):</strong></li></ul><p>Você não está mais limitado à simples igualdade. O LOOKUP JOIN agora permite especificar <strong>múltiplos critérios</strong> para correlação e incorporar uma variedade de <strong>operadores binários,</strong> incluindo ==, !=, &lt;, &gt;, &lt;=, e &gt;=. Isso significa que você pode criar condições de join altamente nuançadas, permitindo que você faça perguntas muito mais sofisticadas sobre seus dados.</p><p>Exemplo 1: encontrando métricas de aplicação com limite de SLA por serviço</p><pre><code>FROM application_metrics
| LOOKUP JOIN sla_thresholds
      ON service_name == sla_service AND response_time > sla_response_time</code></pre><p>Exemplo 2: esta consulta calcula o valor devido com base em políticas regionais de precificação que mudam ao longo do tempo. Ele une três conjuntos de dados baseados em condições complexas de intervalo de datas e igualdade para calcular um <code>due_amount</code> final. A segunda busca usa o campo <code>measurement_date</code> do índice <code>meter_readings</code> e o campo <code>region_id</code> do índice <code>customers</code> para se juntar ao índice <code>pricing_policies</code> e encontrar a política de precificação correta para o <code>region</code> e <code>measurement_date</code> específicos.</p><pre><code>FROM meter_readings
| LOOKUP JOIN customers
      ON meter_id
| LOOKUP JOIN pricing_policies
      ON
        region_id == region AND
          measurement_date >= policy_begin_date AND
          measurement_date < policy_end_date
| EVAL due_amount = (kwh_consumed * rate_per_kwh + base_charge) * (1 + tax_rate)
| EVAL period = policy_name
| KEEP customer_name, period, due_amount, measurement_date, kwh_consumed,
    rate_per_kwh, base_charge, tax_rate
| SORT measurement_date</code></pre><ul><li><strong>Ganhos massivos de desempenho para joins filtrados: </strong></li></ul><p>Melhoramos o desempenho para &quot;joins expansivos&quot; que são filtrados usando condições de tabela de consulta. Os joins expansivos produzem múltiplas correspondências por linha de entrada, o que pode criar grandes conjuntos intermediários de resultados. Isso piora quando muitas dessas linhas são descartadas por um filtro subsequente. Na versão 9.2, otimizamos esses joins filtrando linhas desnecessárias quando um filtro é aplicado aos dados de consulta, evitando processar linhas que seriam descartadas. Em alguns cenários, esses joins podem ser até <strong>1000 vezes mais rápidos</strong>!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/002c014ee29b1aaf9ddeb8c554bb76efe3ed180c-1572x954.png" style="width: 100%;" /><p>Essa otimização é crucial ao lidar com joins em expansão, onde uma pesquisa pode inicialmente gerar muitas correspondências potenciais. Ao aplicar filtros de forma inteligente, apenas os dados relevantes são processados, reduzindo drasticamente o tempo de execução da consulta e permitindo a análise em tempo real em grandes conjuntos de dados. Isso significa que você obtém seus insights muito mais rapidamente, mesmo com operações de join muito grandes ou complexas.</p><p><strong>Compatibilidade do Lookup Join Cross-Cluster Search (CCS):</strong></p><p>Quando o Lookup Join foi lançado como GA nas versões 8.19 e 9.1, ele não era compatível com Pesquisa entre clusters (CCS). Para organizações que operam em vários clusters, o LOOKUP JOIN agora se integra perfeitamente ao CCS na versão 9.2. Basta colocar seu índice de pesquisa em todos os clusters remotos onde você deseja realizar um join, e o ES|QL aproveitará automaticamente esses índices de pesquisa remota para unir seus dados remotos. Isso simplifica a análise distribuída de dados e garante um enriquecimento consistente em toda a sua implantação do Elasticsearch.</p><p>Essas melhorias significam que você pode correlacionar conjuntos de dados diversos com precisão, rapidez e facilidade sem precedentes, revelando insights mais profundos e acionáveis sem soluções complexas ou etapas de pré-processamento.</p><h2>Enriqueça seus dados com facilidade: Kibana Discover UX para Lookup Indices</h2><p>O enriquecimento de dados deve ser simples, não um obstáculo. Introduzimos uma experiência fantástica de usuário no Discover do Kibana para criar e gerenciar índices de lookup.</p><p><strong>Fluxo de trabalho intuitivo:</strong> o preenchimento automático abrangente do Discover guiará você pelo processo, sugerindo índices de pesquisa e campos de união no editor ES|QL, tornando incrivelmente fácil conectar seus dados carregados aos índices existentes. Digite o nome de um índice de consulta que não exista e obtenha acesso direto ao editor de pesquisa com um clique para criar o índice. Digite o nome de um índice de consulta existente e sugeriremos uma opção para editá-lo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d36fd6ffd6bc0bf8d31067f6266445c68d15c71c-1400x184.png" style="width: 100%;" /><p><strong>Gestão em linha (CRUD):</strong> mantenha seus conjuntos de dados de referência atualizados com capacidades de edição em linha (Criar, Ler, Atualizar, Excluir) diretamente no Discover.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a0e4aa66540b1f725c24ccb0519d978415073bb6-1453x842.png" style="width: 100%;" /><p><strong>Upload de arquivos sem esforço: </strong>Agora você pode carregar arquivos diretamente, como CSVs, no Discover e usá-los instantaneamente nos seus <code>LOOKUP JOIN</code>. Chega de alternar entre diferentes áreas do Kibana!</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0f5dc2c712af4c4cada50292a7c8b836eb02aa67-1600x748.png" style="width: 100%;" /><p>Seja no mapeamento de IDs de usuário a nomes, adicionando metadados empresariais ou unindo arquivos de referência estáticos, este recurso democratiza o enriquecimento de dados, colocando a capacidade de melhorar com os joins diretamente nas mãos de cada usuário – rápido, simples e tudo em um só lugar.</p><h2>Mantenha seu contexto: Apresentando INLINE STATS (prévia técnica)</h2><p>Agregar dados é fundamental, mas às vezes você precisa ver os agregados <em>junto com</em> os dados originais. Temos o prazer de apresentar <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by">INLINE STATS</a> como um recurso <strong>de visualização técnica</strong>.</p><p>Ao contrário do comando <code>STATS</code>, que substitui seus campos de entrada pela saída agregada, <code>INLINE STATS</code> preserva todos os campos de entrada originais e simplesmente adiciona os novos campos agregados. Isso permite que você execute outras operações nos campos de entrada originais <em>após</em> a agregação, proporcionando um fluxo de trabalho de análise mais contínuo e flexível.</p><p>Por exemplo, para calcular a distância média de voo mantendo as linhas individuais de voo:</p><pre><code>FROM kibana_sample_data_flights
 | KEEP Carrier, Dest, DistanceMiles
 | INLINE STATS avgDist = ROUND(AVG(DistanceMiles))
       BY Dest
 | WHERE DistanceMiles > avgDist</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6eea943035e0ab371270084c504a06bb89f8b82b-1496x290.png" style="width: 100%;" /><p>Nessa consulta, <code>avgDist</code> é adicionado a cada linha com a <code>Dest</code>(inação) correspondente pela qual agrupamos e, como ainda temos as colunas de informações do voo, podemos filtrar os resultados para os voos com uma distância maior que a média.</p><h2>Compatível com séries temporais no ES|QL (prévia técnica)</h2><p>O Elasticsearch utiliza <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds">fluxos de dados de séries temporais</a> para armazenar métricas. Estamos adicionando suporte para agregações de séries temporais no ES|QL, através do comando <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> source. Isso está disponível no Elastic Cloud Serverless e na versão 9.2 básica como uma prévia técnica.</p><p>A análise de séries temporais é amplamente baseada em consultas de agregação que resumem os valores das métricas em buckets de tempo, divididos por uma ou mais dimensões de filtragem. A maioria das consultas de agregação depende do processamento em duas etapas, com (a) uma função de agregação interna resumindo valores por série temporal e (b) uma função de agregação externa, combinando os resultados de (a) em todas as séries temporais.</p><p>O comando <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/ts"><code>TS</code></a> source, combinado com <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/stats-by"><code>STATS</code></a>, fornece uma forma concisa, porém eficaz, de expressar tais consultas ao longo de séries temporais. Mais concretamente, considere o seguinte exemplo para calcular a taxa total de solicitações por host e por hora:</p><pre><code>TS my_metrics
| WHERE @timestamp > NOW() - 1 day
| STATS SUM(RATE(requests))
      BY host, TBUCKET(1h)</code></pre><p>Nesse caso, a função de agregação de séries temporais <code>RATE</code> é avaliada primeiro por série temporal e hora. Os agregados parciais produzidos são então combinados usando <code>SUM</code> para calcular os valores agregados finais por hospedado e por hora.</p><p>Você pode conferir a lista de funções de agregação de séries temporais disponíveis <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/time-series-aggregation-functions">aqui</a>. A taxa de <a href="https://www.elastic.co/docs/manage-data/data-store/data-streams/time-series-data-stream-tsds#time-series-metric">contador</a> agora é compatível, provavelmente a função de agregação mais importante para processar contadores.</p><p>O comando <code>TS</code> source é projetado para ser combinado com <code>STATS</code>, com execução ajustada para ser compatível com agregações de séries temporais de forma eficiente. Por exemplo, os dados são ordenados antes de serem inseridos no <code>STATS</code>. Comandos de processamento que possam enriquecer ou alterar os dados de série temporal ou sua ordem, como <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork"><code>FORK</code></a> ou <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/inlinestats-by"><code>INLINE STATS</code></a>, atualmente não são permitidos entre <code>TS</code> e <code>STATS</code>. Essa limitação pode ser eliminada no futuro.</p><p>A saída tabular <code>STATS</code> pode ser processada ainda com qualquer comando aplicável. Por exemplo, a consulta a seguir calcula a razão entre a média <code>cpu_usage</code> por host e hora para o valor máximo por host:</p><pre><code>TS my_metrics
| STATS avg_usage = AVG(AVG_OVER_TIME(cpu_usage))
      BY host, time_bucket = TBUCKET(1h)
| INLINE STATS max_avg_usage = MAX(avg_usage)
      BY host
| EVAL ratio = avg_usage / max_avg_usage
| KEEP host, time_bucket, ratio
| SORT host, time_bucket DESC</code></pre><p>Os dados de série temporal são armazenados em nosso mecanismo de armazenamento colunar subjacente, que é alimentado pelos valores de documento do Lucene. O comando TS adiciona execução vetorizada de consultas através do mecanismo de computação ES|QL. O desempenho das consultas geralmente é melhorado em mais de uma ordem de magnitude, em comparação com consultas <a href="https://www.elastic.co/docs/reference/query-languages/querydsl">DSL</a> equivalentes, e está em pé de igualdade com sistemas estabelecidos e específicos para métricas. No futuro, forneceremos uma análise detalhada de arquitetura e desempenho, então fique ligado.</p><h2>Expandindo seu conjunto de ferramentas: novas funções ES|QL</h2><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p><strong>Manipulação de string: </strong><a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-contains">CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/mv-functions#esql-mv_contains">MV_CONTAINS</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode">URL_ENCODE</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_encode_component">URL_ENCODE_COMPONENT</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/string-functions#esql-url_decode">URL_DECODE</a> para processamento mais robusto de texto e URL.</p><p><strong>Séries temporais e geoespaciais:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/grouping-functions#esql-tbucket">TBUCKET</a> para buckets flexíveis de tempo, TO_DENSE_VECTOR para operações vetoriais e um conjunto abrangente de <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/spatial-functions">funções geoespaciais</a> como <code>ST_GEOHASH</code>, <code>ST_GEOTILE</code>, <code>ST_GEOHEX</code>, <code>TO_GEOHASH</code>, <code>TO_GEOTILE</code>, <code>TO_GEOHEX</code> para análise avançada baseada em localização.</p><p><strong>Formatação de datas:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-day_name">DAY_NAME</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/date-time-functions#esql-month_name">MONTH_NAME</a> para representações de data mais legíveis.</p><p>Essas funções oferecem um conjunto mais rico de ferramentas para manipular e analisar seus dados diretamente dentro do ES|QL.</p><h2>Sob o capô: mais desempenho e eficiência</h2><p>Além dos recursos destacados, o Elasticsearch 9.2 inclui diversas otimizações de desempenho em todo o ES|QL. Aceleramos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/where#like-and-rlike">RLIKE (LIST</a>) com pushdown nos casos em que a função substitui várias consultas RLIKE semelhantes. Com <code>RLIKE</code> (LIST), podemos unir essas consultas em um único autômato e aplicar um autômato em vez de vários. Também temos carregamento mais rápido dos campos de palavras-chave com ordenação de índice e otimizações gerais de consultas – essas melhorias garantem que suas consultas ES|QL sejam executadas de forma mais eficiente do que nunca.</p><h2>Comece hoje mesmo!</h2><p>Elasticsearch 9.2 representa um salto significativo para o ES|QL, trazendo uma melhoria e flexibilidade sem precedentes para seus fluxos de trabalho de análise de dados. Incentivamos você a explorar esses novos recursos e experimentar a diferença que eles fazem.</p><p>Para uma lista abrangente de todas as mudanças e aprimoramentos no Elasticsearch 9.2, consulte as <a href="https://www.elastic.co/guide/en/elasticsearch/reference/9.2/release-notes-9.2.0.html">notas de lançamento oficiais</a>. Boas consultas!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criar um conector do ChatGPT com o Elasticsearch para consultar problemas no GitHub]]></title>
            <description><![CDATA[Saiba como criar um conector ChatGPT personalizado e implantar um servidor Elasticsearch MCP que usa a pesquisa híbrida para buscar problemas internos do GitHub.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/chatgpt-connector-mcp-server-github-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/chatgpt-connector-mcp-server-github-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
            <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Recentemente, a OpenAI anunciou o recurso de <a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt">conectores personalizados</a> para o ChatGPT nos planos Pro/Business/Empresarial e Edu. Além dos conectores prontos para uso para acessar dados no Gmail, GitHub, Dropbox etc. É possível criar conectores personalizados usando servidores MCP.</p><p>Os conectores personalizados permitem que você combine seus conectores ChatGPT existentes com fontes adicionais de dados, como o Elasticsearch, para obter respostas abrangentes.</p><p>Neste artigo, criaremos um servidor <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a> que conecta o ChatGPT a um índice Elasticsearch contendo informações sobre problemas internos e solicitações de pull do GitHub. Isso permite que consultas em linguagem natural sejam respondidas usando os dados do seu Elasticsearch.</p><p>Implantaremos o servidor MCP usando o <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a> no Google Colab com ngrok para obter um URL público ao qual o ChatGPT possa se conectar, eliminando a necessidade de uma configuração de infraestrutura complexa.</p><p>Para uma visão geral do MCP e seu ecossistema, consulte <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">O Estado Atual do MCP</a>.</p><h2>Pré-requisitos</h2><p>Antes de começar, você precisará de:</p><ul><li>Cluster do Elasticsearch (8.X ou superior)</li><li>Chave de API do Elasticsearch com acesso de leitura ao seu índice</li><li>Conta do Google (para o Google Colab)</li><li>Conta Ngrok (versão gratuita funciona)</li><li>Conta do ChatGPT com plano Pro/Empresarial/Business ou Edu</li></ul><h2>Entendendo os requisitos do conector MCP do ChatGPT</h2><p>Os conectores MCP do ChatGPT exigem a implementação de duas ferramentas: <code>search</code> e <code>fetch</code>. Para mais detalhes, consulte <a href="https://platform.openai.com/docs/mcp#create-an-mcp-server">OpenAI Docs</a>.</p><h3><a href="https://platform.openai.com/docs/mcp#search-tool">Ferramenta de busca</a></h3><p>Retorna uma lista de resultados relevantes do seu índice Elasticsearch com base em uma consulta do usuário.</p><h4>O que ele recebe:</h4><ul><li>Uma única string com a consulta de linguagem natural do usuário.</li><li>Exemplo: &quot;Encontre problemas relacionados à migração do Elasticsearch.&quot;</li></ul><h4>O que ele retorna: </h4><ul><li>Um objeto com uma chave <code>result</code> contendo um array de objetos de resultado. Cada resultado inclui:<ul><li><code>id</code> - Identificador único do documento</li><li><code>title</code> - Título da issue ou do PR</li><li><code>url</code> - Link para o problema/PR</li></ul></li></ul><h4>Na nossa implementação:</h4><pre><code>return {
    "results": [
        {
            "id": "PR-612",
            "title": "Fix memory leak in WebSocket notification service",
            "url": "https://internal-git.techcorp.com/pulls/612"
        },
        # ... more results
    ]
}</code></pre><h3><a href="https://platform.openai.com/docs/mcp#fetch-tool">Ferramenta de recuperação</a></h3><p>Recupera o conteúdo completo de um documento específico.</p><h4>O que ele recebe:</h4><ul><li>Uma única string com o ID do documento Elasticsearch do resultado de busca</li><li>Exemplo: &quot;Me dê os detalhes do PR-578.&quot;</li></ul><h4>O que ele retorna:</h4><ul><li>Um objeto de documento completo com:<ul><li><code>id</code> - Identificador único do documento</li><li><code>title</code> - Título da issue ou do PR</li><li><code>text</code> - Complete a descrição e os detalhes do problema/PR</li><li><code>url</code> - Link para o problema/PR</li><li><code>type</code> - Tipo de documento (issue, pull_request)</li><li><code>status</code> - Status atual (aberto, em_andamento, resolvido)</li><li><code>priority</code> - Nível de prioridade (baixo, médio, alto, crítico)</li><li><code>assignee</code> - Pessoa designada para o problema/PR</li><li><code>created_date</code> - Quando foi criado</li><li><code>resolved_date</code> - Quando foi resolvido (se aplicável)</li><li><code>labels</code> - Tags associadas ao documento</li><li><code>related_pr</code> - ID de pull request relacionado</li></ul></li></ul><pre><code>return {
    "id": "PR-578",
    "title": "Security hotfix: Patch SQL injection vulnerabilities",
    "text": "Description: CRITICAL SECURITY FIX for ISSUE-1889. Patches SQL...",
    "url": "https://internal-git.techcorp.com/pulls/578",
    "type": "pull_request",
    "status": "closed",
    "priority": "critical",
    "assignee": "sarah_dev",
    "created_date": "2025-09-19",
    "resolved_date": "2025-09-19",
    "labels": "security, hotfix, sql",
    "related_pr": null
}</code></pre><p><strong>Observação</strong>: este exemplo usa uma estrutura plana onde todos os campos estão no nível raiz. Os requisitos do OpenAI são flexíveis e também permitem objetos de metadados aninhados.</p><h2>Questões do GitHub e conjunto de dados PRs</h2><p>Para este tutorial, vamos usar um conjunto de dados interno do GitHub contendo problemas e solicitações de pull. Isso representa um cenário em que você deseja consultar dados privados e internos por meio do ChatGPT.</p><p>O conjunto de dados pode ser encontrado <a href="https://gist.github.com/TomasMurua/4e7bbdf7a7ebbdffaa663c43578d934a">aqui</a>. E atualizaremos o índice dos dados usando a <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">bulk API</a>.</p><p>Esse conjunto de dados inclui:</p><ul><li>Problemas com descrições, status, prioridade e responsáveis</li><li>Solicitações de pull com alterações de código, revisões e informações de implantação</li><li>Relações entre problemas e PRs (por exemplo, PR-578 corrige o ISSUE-1889)</li><li>Rótulos, datas e outros metadados</li></ul><h3>Mapeamentos de índice</h3><p>O índice usa os seguintes <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mapeamentos</a> para permitir a pesquisa híbrida com o <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>. A <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">text_semantic</a> é usada para busca semântica, enquanto outros campos permitem a busca por palavras-chave.</p><pre><code>{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_semantic": {
        "type": "semantic_text",
        "inference_id": ".elser-2-elasticsearch"
      },
      "url": {
        "type": "keyword"
      },
      "type": {
        "type": "keyword"
      },
      "status": {
        "type": "keyword"
      },
      "priority": {
        "type": "keyword"
      },
      "assignee": {
        "type": "keyword"
      },
      "created_date": {
        "type": "date",
        "format": "iso8601"
      },
      "resolved_date": {
        "type": "date",
        "format": "iso8601"
      },
      "labels": {
        "type": "keyword"
      },
      "related_pr": {
        "type": "keyword"
      }
    }
  }
}</code></pre><h2>Construa o servidor MCP</h2><p>Nosso servidor MCP implementa duas ferramentas seguindo as especificações da OpenAI, usando busca híbrida para combinar correspondência semântica e de texto para obter melhores resultados.</p><h3>Ferramenta de busca</h3><p>Utiliza busca híbrida com <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> (Reciprocal Rank Fusion), combinando buscar semântica com correspondência de texto:</p><pre><code>@mcp.tool()
    async def search(query: str) -> Dict[str, List[Dict[str, Any]]]:
        """
        Search for internal issues and PRs using hybrid search (semantic + text with RRF).
        Returns list with id, title, and url per OpenAI spec.
        """
        if not query or not query.strip():
            return {"results": []}

        logger.info(f"Searching for: '{query}'")

        try:
            # Hybrid search with RRF (Reciprocal Rank Fusion)
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                size=10,
                source=["id", "title", "url", "type", "priority"],
                retriever={
                    "rrf": {
                        "retrievers": [
                            {
                                # Semantic search with ELSER
                                "standard": {
                                    "query": {
                                        "semantic": {
                                            "field": "text_semantic",
                                            "query": query
                                        }
                                    }
                                }
                            },
                            {
                                # Text search (BM25) for keyword matching
                                "standard": {
                                    "query": {
                                        "multi_match": {
                                            "query": query,
                                            "fields": [
                                                "title^3",
                                                "text^2",
                                                "assignee^2",
                                                "type",
                                                "labels",
                                                "priority"
                                            ],
                                            "type": "best_fields",
                                            "fuzziness": "AUTO"
                                        }
                                    }
                                }
                            }
                        ],
                        "rank_window_size": 50,
                        "rank_constant": 60
                    }
                }
            )

            results = []
            if response and 'hits' in response:
                for hit in response['hits']['hits']:
                    source = hit['_source']
                    results.append({
                        "id": source.get('id', hit['_id']),
                        "title": source.get('title', 'Unknown'),
                        "url": source.get('url', '')
                    })

            logger.info(f"Found {len(results)} results")
            return {"results": results}

        except Exception as e:
            logger.error(f"Search error: {e}")
            raise ValueError(f"Search failed: {str(e)}")</code></pre><h3>Pontos principais:</h3><ul><li><strong>Busca híbrida com RRF:</strong> combina busca semântica (ELSER) e busca por texto (BM25) para melhores resultados.</li><li><strong>Consulta multi-correspondência:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query">busca em múltiplos campos</a> com aumento de relevância (title^3, text^2, assignee^2). O símbolo de caret (^) multiplica as pontuações de relevância, priorizando as correspondências nos títulos em detrimento do conteúdo.</li><li><strong>Correspondência inexata:</strong> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#fuzziness"><code>fuzziness: AUTO</code></a> lida com erros de digitação e ortografia, permitindo correspondências aproximadas.</li><li><strong>Ajuste dos parâmetros do RRF:</strong><ul><li><code>rank_window_size: 50</code> - Especifica quantos resultados principais de cada recuperador (semântico e textual) são considerados antes da mesclagem.</li><li><code>rank_constant: 60</code> - Esse valor determina quanta influência os documentos em conjuntos de resultados individuais têm sobre o resultado final classificado.</li></ul></li><li><strong>Retorna somente os campos obrigatórios:</strong> <code>id</code>, <code>title</code>, <code>url</code> de acordo com a especificação da OpenAI e evita a exposição desnecessária de campos adicionais.</li></ul><h3>Ferramenta de recuperação</h3><p>Recupera detalhes do documento pelo ID do documento, quando existe:</p><pre><code>@mcp.tool()
    async def fetch(id: str) -> Dict[str, Any]:
        """
        Retrieve complete issue/PR details by ID.
        Returns id, title, text, url.
        """
        if not id:
            raise ValueError("ID is required")

        logger.info(f"Fetching: {id}")

        try:
            # Search by the 'id' field (not _id) since IDs are stored as a field
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                body={
                    "query": {
                        "term": {
                            "id": id  # Search by your custom 'id' field
                        }
                    },
                    "size": 1
                }
            )

            if not response or not response['hits']['hits']:
                raise ValueError(f"Document with id '{id}' not found")

            hit = response['hits']['hits'][0]
            source = hit['_source']

            result = {
                "id": source.get('id', id),
                "title": source.get('title', 'Unknown'),
                "text": source.get('text', ''),
                "url": source.get('url', ''),
                "type": source.get('type', ''),
                "status": source.get('status', ''),
                "priority": source.get('priority', ''),
                "assignee": source.get('assignee', ''),
                "created_date": source.get('created_date', ''),
                "resolved_date": source.get('resolved_date', ''),
                "labels": source.get('labels', ''),
                "related_pr": source.get('related_pr', '')
            }

            logger.info(f"Fetched: {result['title']}")
            return result

        except Exception as e:
            logger.error(f"Fetch error: {e}")
            raise ValueError(f"Failed to fetch '{id}': {str(e)}")</code></pre><h3>Pontos principais:</h3><ul><li><strong>Buscar por campo de ID do documento:</strong> Utiliza consulta de termo no campo personalizado <code>id</code></li><li><strong>Retorna o documento completo:</strong> inclui o campo <code>text</code> completo com todo o conteúdo</li><li><strong>Estrutura plana:</strong> Todos os campos no nível da raiz, correspondendo à estrutura de documentos do Elasticsearch.</li></ul><h2>Implantar no Google Colab</h2><p>Usaremos o Google Colab para executar nosso servidor MCP e o ngrok para expô-lo publicamente, permitindo que o ChatGPT se conecte a ele.</p><h3>Etapa 1: Abra o notebook do Google Colab</h3><p>Acesse nosso notebook pré-configurado <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-chatgpt-connector">Elasticsearch MCP para ChatGPT</a>.</p><h3>Etapa 2: Configure suas credenciais</h3><p>Você precisará de três informações:</p><ul><li><strong>URL do Elasticsearch:</strong> seu <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise/connect-elasticsearch">URL do cluster do Elasticsearch</a>.</li><li><strong>Chave da API do Elasticsearch:</strong> <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">Chave da API</a> com permissão de leitura do seu índice.</li><li><strong>Token de autenticação Ngrok:</strong> token grátis do <a href="https://ngrok.com/">ngrok</a>. Vamos usar o ngrok para expor a URL do MCP à internet para que o ChatGPT possa se conectar a ela.</li></ul><h4>Obter seu token ngrok</h4><ol><li>Cadastre-se para uma conta gratuita em <a href="https://ngrok.com/">ngrok</a></li><li>Acesse seu <a href="https://dashboard.ngrok.com/">painel do ngrok</a></li><li>Copie seu token de autenticação.</li></ol><h4>Adicionando segredos ao Google Colab</h4><p>No notebook do Google Colab:</p><ol><li>Clique no <strong>ícone de chave </strong>na barra lateral esquerda para abrir <strong>Secrets</strong>.</li><li>Adicione estes três segredos:</li></ol><pre><code>ELASTICSEARCH_URL=https://your-cluster.elastic.com:443
ELASTICSEARCH_API_KEY=your-api-key
NGROK_TOKEN=your-ngrok-token</code></pre><p>3. Habilitar o acesso ao notebook para cada segredo</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5dd6ac19fe816a562c6351fdb0f11369da0e877-609x321.jpg" style="width: 100%;" /><h3>Passo 3: Execute o notebook</h3><ol><li>Clique em <strong>Runtime</strong> e depois em <strong>Executar tudo</strong> para executar todas as células</li><li>Aguarde o servidor iniciar (cerca de 30 segundos)</li><li>Procure a saída mostrando seu URL público do ngrok</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f185100e8869624bc9e1c7b2b4eb32785e2d89e7-1189x283.png" style="width: 100%;" /><p>4. A saída exibirá algo como:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e02e625e91ed9136454e4401b184575fb03a336e-1052x465.jpg" style="width: 100%;" /><h2>Conectar-se ao ChatGPT</h2><p>Agora vamos conectar o servidor MCP à sua conta do ChatGPT.</p><ol><li>Abra o ChatGPT e vá para <strong>Configurações</strong>.</li><li>Navegue até <strong>Conectores. </strong>Se você estiver usando uma conta Pro, precisará ativar <a href="https://platform.openai.com/docs/guides/developer-mode">o modo de desenvolvedor</a> nos conectores.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32c02192912fc0e7e5a52e9399077ba7ae3b4901-739x715.png" style="width: 100%;" /><p><em>Se você está usando o ChatGPT em empresas ou negócios, precisa disponibilizar o conector para seu local de trabalho.</em></p><p>3. Clique em <strong>Criar</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/15c53e5ccc381108a9dc0052cca05bf0fc97679a-755x683.png" style="width: 100%;" /><p><em><strong>Observação</strong>: nos espaços de trabalho Business, Empresarial e Edu, somente os proprietários, administradores e usuários com a respectiva configuração ativada (para Empresarial/Edu) podem adicionar conectores personalizados. Usuários com a função de membro padrão não têm permissão para adicionar conectores personalizados.</em></p><p><em>Após um conector ser adicionado e habilitado por um proprietário ou usuário administrador, ele fica disponível para todos os membros do espaço de trabalho.</em></p><p>4. Insira as informações necessárias e sua URL ngrok que termina em <code>/sse/</code>. Repare no &quot;/&quot; após &quot;sse&quot;. Não vai funcionar sem ele:</p><ul><li><strong>Nome:</strong> Elasticsearch MCP</li><li><strong>Descrição: </strong>MCP personalizado para pesquisar e recuperar informações internas do GitHub.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/162a85705cc8ac48a3f2f665551d513e0719f93d-479x684.png" style="width: 100%;" /><p>5. Pressione <strong>Criar</strong> para salvar o MCP personalizado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97eb5fb0a32b86bfadfb35561f698616f217c049-913x629.png" style="width: 100%;" /><p>A conexão será instantânea se seu servidor estiver em execução. Não é necessária autenticação adicional, pois a chave da API do Elasticsearch está configurada no seu servidor.</p><h2>Teste o servidor MCP</h2><p>Antes de fazer perguntas, você precisa selecionar qual conector o ChatGPT deve usar.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" style="width: 100%;" /><h3>Prompt 1: Buscar por problemas</h3><p>Pergunte: &quot;<strong>Encontre problemas relacionados à migração do Elasticsearch&quot; </strong>e confirme a chamada da ferramenta de ações.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cfd781acbff8cd7c8095bbe29224f8b26d581f77-650x375.png" style="width: 100%;" /><p>O ChatGPT chamará a ferramenta <code>search</code> com sua consulta. Você pode ver que ele está procurando as ferramentas disponíveis, se preparando para chamar a ferramenta Elasticsearch e confirma com o usuário antes de tomar qualquer medida em relação à ferramenta.</p><h4>Solicitação de chamada de ferramenta:</h4><pre><code>{
  "query": "Elasticsearch migration issues"
}</code></pre><h4>Resposta da ferramenta:</h4><pre><code>{
  "results": [
    {
      "id": "PR-598",
      "title": "Elasticsearch 8.x migration - Application code changes",
      "url": "https://internal-git.techcorp.com/pulls/598"
    },
    {
      "id": "ISSUE-1712",
      "title": "Migrate from Elasticsearch 7.x to 8.x",
      "url": "https://internal-git.techcorp.com/issues/1712"
    },
    {
      "id": "RFC-045",
      "title": "Design Proposal: Microservices Migration Architecture",
      "url": "https://internal-git.techcorp.com/rfcs/045"
    }
    // ... 7 more results
  ]
}</code></pre><p>O ChatGPT processa os resultados e os apresenta em um formato natural e conversacional.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d5b6cff85c7e54ccc2584b8ae96d45495fae8c1-923x1352.png" style="width: 100%;" /><h3>Nos bastidores</h3><h4>Prompt: &quot;Encontrar problemas relacionados à migração do Elasticsearch&quot;</h4><p>1. Chamadas do ChatGPT <code>search(“Elasticsearch migration”)</code></p><p>2. O Elasticsearch realiza uma busca híbrida</p><ul><li><strong>A busca semântica</strong> compreende conceitos como &quot;atualização&quot; e &quot;<em>compatibilidade de versões&quot;</em>.</li><li>A <strong>busca de texto</strong> encontra correspondências exatas para &quot;<em>Elasticsearch</em>&quot; e &quot;migração&quot;.</li><li>O <strong>RRF</strong> combina e classifica os resultados de ambas as abordagens</li></ul><p>3. Retorna os 10 melhores eventos de correspondência com <code>id</code>, <code>title</code>, <code>url</code></p><p>4. O ChatGPT identifica &quot;<em>ISSUE-1712: migrar do Elasticsearch 7.x para o 8.x</em>&quot; como o resultado mais relevante</p><h3>Prompt 2: Obter todos os detalhes</h3><p>Perguntar: <strong><em>&quot;Informe detalhes sobre o ISSUE-1889&quot;</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5c0db5245535ce67a36056e61e135bddc97ce496-934x629.png" style="width: 100%;" /><p>O ChatGPT reconhece que você quer informações detalhadas sobre um problema específico e aciona a ferramenta <code>fetch</code>, confirmando com o usuário antes de tomar qualquer medida em relação à ferramenta.</p><h4>Solicitação de chamada de ferramenta:</h4><pre><code>{
  "id": "ISSUE-1889"
}</code></pre><h4>Resposta da ferramenta:</h4><pre><code>{
  "id": "ISSUE-1889",
  "title": "SQL injection vulnerability in search endpoint",
  "text": "Description: Security audit identified SQL injection vulnerability in /api/v1/search endpoint. User input from query parameter is not properly sanitized before being used in raw SQL query. Severity: HIGH - Immediate action required Affected Code: - File: services/search/query_builder.py - Line: 145-152 - Issue: String concatenation used instead of parameterized queries Investigation: - @security_team_alice: Confirmed exploitable with UNION-based injection - @sarah_dev: Checking all other endpoints for similar patterns - @john_backend: Found 3 more instances in legacy codebase Remediation: - Rewrite using SQLAlchemy ORM or parameterized queries - Add input validation and sanitization - Implement WAF rules as additional layer - Security regression tests Comments: - @tech_lead_mike: Stop all other work, this is P0 - @sarah_dev: PR-578 ready with fixes for all 4 vulnerable endpoints - @alex_devops: Deployed hotfix to production 2025-09-19 at 14:30 UTC - @security_team_alice: Verified fix, conducting full pentest next week Resolution: All vulnerable endpoints patched. Added pre-commit hooks to catch raw SQL queries. Security training scheduled for team.",
  "url": "https://internal-git.techcorp.com/issues/1889",
  "type": "issue",
  "status": "closed",
  "priority": "critical",
  "assignee": "sarah_dev",
  "created_date": "2025-09-18",
  "resolved_date": "2025-09-19",
  "labels": "security, vulnerability, bug, sql",
  "related_pr": "PR-578"
}</code></pre><p>O ChatGPT sintetiza as informações e as apresenta claramente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/410f19f213e94fc4e3c47eeef6e04b69e0c86159-602x462.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/54d8ffa117628a1e3afc317c3ab75d4f7731d7ab-767x1600.png" style="width: 100%;" /><h3>Nos bastidores</h3><h4>Prompt: “Informe mais detalhes sobre ISSUE-1889”</h4><ol><li>Chamadas do ChatGPT <code>fetch(“ISSUE-1889”)</code></li><li>O Elasticsearch recupera o documento completo</li><li>Retorna um documento completo com todos os campos no nível raiz</li><li>O ChatGPT sintetiza as informações e responde com citações adequadas.</li></ol><h2>Conclusão</h2><p>Neste artigo, criamos um servidor MCP personalizado que conecta o ChatGPT ao Elasticsearch usando ferramentas MCP dedicadas de <strong>busca</strong> e <strong>recuperação</strong>, permitindo consultas em linguagem natural sobre dados privados.</p><p>Este padrão MCP funciona para qualquer índice Elasticsearch, documentação, produtos, log ou quaisquer outros dados que você queira consultar por meio de linguagem natural.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Desenvolvimento de um assistente RAG agente usando LangChain e Elasticsearch]]></title>
            <description><![CDATA[Aprenda como construir um assistente de notícias interativo usando LangChain e Elasticsearch que responde a consultas sobre artigos com roteamento adaptativo.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agentic-rag-news-assistant-langchain-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agentic-rag-news-assistant-langchain-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kirti Sodhi]]></dc:creator>
            <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d7213956309c6b5863627aca2ba95569b02fc4c7-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Este artigo do blog explora os fluxos de trabalho RAG com agentes, explicando suas principais características e padrões de design comuns. Além disso, demonstra como implementar esses fluxos de trabalho por meio de um exemplo prático que utiliza o Elasticsearch como repositório de vetores e o LangChain para construir a estrutura RAG agentiva. Por fim, o artigo discute brevemente as melhores práticas e os desafios associados ao projeto e à implementação de tais arquiteturas. Você pode acompanhar o passo a passo para criar um pipeline RAG simples e agético com este <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">notebook Jupyter</a>.</p><h2>Introdução ao RAG agentivo</h2><p>A Geração Aumentada por Recuperação (<a href="https://www.elastic.co/docs/solutions/search/rag">RAG</a>, na sigla em inglês) tornou-se um pilar fundamental em aplicações baseadas em Modelos de Aprendizagem Baseados em Aprendizagem (LLM, na sigla em inglês), permitindo que os modelos forneçam respostas otimizadas ao recuperar o contexto relevante com base nas consultas do usuário. Os sistemas RAG aprimoram a precisão e o contexto das respostas do LLM (Modelo de Aprendizagem Baseado em Aprendizagem) ao utilizar informações externas provenientes de APIs ou bancos de dados, em vez de se limitarem ao conhecimento pré-treinado do LLM. Por outro lado, os agentes de IA operam de forma autônoma, tomando decisões e executando ações para atingir seus objetivos designados.</p><p>O RAG Agentic é uma estrutura que unifica os pontos fortes da geração aumentada por recuperação e do raciocínio agentivo. Ele integra o RAG ao processo de tomada de decisão do agente, permitindo que o sistema escolha dinamicamente fontes de dados, refine consultas para melhor recuperação de contexto, gere respostas mais precisas e aplique um ciclo de feedback para melhorar continuamente a qualidade da saída.</p><h2>Principais características do RAG agentivo</h2><p>A estrutura RAG agentiva representa um grande avanço em relação aos sistemas RAG tradicionais. Em vez de seguir um processo de recuperação fixo, utiliza agentes dinâmicos capazes de planejar, executar e otimizar resultados em tempo real.</p><p>Vamos analisar algumas das principais características que distinguem os pipelines RAG agentivos:</p><ul><li><strong>Tomada de decisão dinâmica</strong>: o Agentic RAG utiliza um mecanismo de raciocínio para compreender a intenção do usuário e direcionar cada consulta para a fonte de dados mais relevante, produzindo respostas precisas e contextualizadas.</li><li><strong>Análise abrangente de consultas:</strong> o Agentic RAG analisa profundamente as consultas dos usuários, incluindo subperguntas e sua intenção geral. Ele avalia a complexidade da consulta e seleciona dinamicamente as fontes de dados mais relevantes para recuperar informações, garantindo respostas precisas e completas.</li><li><strong>Colaboração em múltiplas etapas</strong>: Esta estrutura permite a colaboração em múltiplas etapas através de uma rede de agentes especializados. Cada agente lida com uma parte específica de um objetivo maior, trabalhando sequencialmente ou simultaneamente para alcançar um resultado coeso.</li><li><strong>Mecanismos de autoavaliação</strong>: O pipeline RAG agentivo utiliza a autorreflexão para avaliar os documentos recuperados e as respostas geradas. Ele pode verificar se as informações recuperadas respondem completamente à consulta e, em seguida, revisar a saída quanto à precisão, integridade e consistência factual.</li><li><strong>Integração com ferramentas externas</strong>: Este fluxo de trabalho pode interagir com APIs externas, bancos de dados e fontes de informação em tempo real, incorporando informações atualizadas e adaptando-se dinamicamente à evolução dos dados.</li></ul><h2>Padrões de fluxo de trabalho do RAG agente</h2><p>Os padrões de fluxo de trabalho definem como a IA agente estrutura, gerencia e orquestra aplicações baseadas em LLM de maneira confiável e eficiente. Diversas estruturas e plataformas, como <a href="https://www.langchain.com/">LangChain</a>, <a href="https://www.langchain.com/langgraph">LangGraph</a>, <a href="https://www.crewai.com/">CrewAI</a> e <a href="https://www.llamaindex.ai/">LlamaIndex</a>, podem ser usadas para implementar esses fluxos de trabalho com agentes.</p><ol><li><strong>Cadeia de recuperação sequencial</strong>: Os fluxos de trabalho sequenciais dividem tarefas complexas em etapas simples e ordenadas. Cada etapa melhora a entrada para a próxima, levando a melhores resultados. Por exemplo, ao criar um perfil de cliente, um agente pode extrair detalhes básicos de um CRM, outro recupera o histórico de compras de um banco de dados de transações e um agente final combina essas informações para gerar um perfil completo para recomendações ou relatórios.</li><li><strong>Cadeia de roteamento e recuperação</strong>: Neste padrão de fluxo de trabalho, um agente de roteamento analisa a entrada e a direciona para o processo ou fonte de dados mais apropriada. Essa abordagem é particularmente eficaz quando existem múltiplas fontes de dados distintas com sobreposição mínima. Por exemplo, em um sistema de atendimento ao cliente, o agente de roteamento categoriza as solicitações recebidas, como problemas técnicos, reembolsos ou reclamações, e as encaminha para o departamento apropriado para um tratamento eficiente.</li><li><strong>Cadeia de recuperação paralela</strong>: Neste padrão de fluxo de trabalho, várias subtarefas independentes são executadas simultaneamente e suas saídas são posteriormente agregadas para gerar uma resposta final. Essa abordagem reduz significativamente o tempo de processamento e aumenta a eficiência do fluxo de trabalho. Por exemplo, em um fluxo de trabalho paralelo de atendimento ao cliente, um agente recupera solicitações anteriores semelhantes, enquanto outro consulta artigos relevantes da base de conhecimento. Um agregador combina então esses resultados para gerar uma resolução abrangente.</li><li><strong>Cadeia de trabalho do Orchestrator</strong>: Este fluxo de trabalho compartilha semelhanças com a paralelização devido à sua utilização de subtarefas independentes. No entanto, uma distinção fundamental reside na integração de um agente orquestrador. Este agente é responsável por analisar as consultas do usuário, segmentá-las dinamicamente em subtarefas durante a execução e identificar os processos ou ferramentas apropriados necessários para formular uma resposta precisa.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ece6fc2403f211556c93e99d5227bfb7053b0c31-1600x1047.png" style="width: 100%;" /><h2>Construindo um pipeline RAG agético do zero.</h2><p>Para ilustrar os princípios do RAG agentivo, vamos projetar um fluxo de trabalho usando LangChain e Elasticsearch. Este fluxo de trabalho adota uma arquitetura baseada em roteamento, onde múltiplos agentes colaboram para analisar consultas, recuperar informações relevantes, avaliar resultados e gerar respostas coerentes. Você pode consultar este <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">notebook Jupyter</a> para acompanhar este exemplo.</p><p>O fluxo de trabalho começa com o agente de roteamento, que analisa a consulta do usuário para selecionar o método de recuperação ideal, ou seja, uma abordagem <code>vectorstore</code>, <code>websearch</code> ou <code>composite</code> . O vectorstore lida com a recuperação tradicional de documentos baseada em RAG, a pesquisa na web busca as informações mais recentes que não estão armazenadas no vectorstore, e a abordagem composta combina ambas quando são necessárias informações de múltiplas fontes.</p><p>Se os documentos forem considerados adequados, o agente de sumarização gera uma resposta clara e contextualizada. No entanto, se os documentos forem insuficientes ou irrelevantes, o agente de reescrita de consultas reformula a consulta para melhorar a pesquisa. Essa consulta revisada reinicia o processo de roteamento, permitindo que o sistema refine sua busca e aprimore o resultado final.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ed8701a7f15558fbf2e967a884b3e770eccb826b-1256x1092.png" style="width: 100%;" /><h3>Pré-requisitos</h3><p>Este fluxo de trabalho depende dos seguintes componentes principais para executar o exemplo de forma eficaz:</p><ul><li>Python 3.10</li><li><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Notebook Jupyter</a></li><li>Azure OpenAI</li><li>Elasticsearch</li><li>LangChain</li></ul><p>Antes de prosseguir, você será solicitado a configurar o seguinte conjunto de variáveis de ambiente obrigatórias para este exemplo.</p><pre><code>AZURE_OPENAI_ENDPOINT="Add your azure openai endpoint"
AZURE_OPENAI_KEY="Add your azure openai key"
AZURE_OPENAI_DEPLOYMENT="gpt-4.1"
AZURE_OPENAI_API_VERSION="Add your azure openai api version"

ES_ENDPOINT = "Add your Elasticsearch ENDPOINT"
ES_API_KEY = "Add your Elasticsearch API KEY"</code></pre><h3>Fontes de dados</h3><p>Este fluxo de trabalho é ilustrado usando um subconjunto do conjunto de dados da AG News. O conjunto de dados inclui artigos de notícias de diversas categorias, como Internacional, Esportes, Negócios e Ciência/Tecnologia.</p><pre><code>dataset = load_dataset("ag_news", split="train[:1000]")
docs = [
    Document(
        page_content=sample["text"],
        metadata={"category": sample["label"]}
    )
    for sample in dataset
]</code></pre><p>O <a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">módulo ElasticsearchStore</a> é utilizado a partir do <code>langchain_elasticsearch</code> como nosso armazenamento de vetores. Para a recuperação de dados, implementamos a SparseVectorStrategy, utilizando <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">o ELSER</a>, o modelo de incorporação proprietário da Elastic. É essencial confirmar se o modelo ELSER está instalado e implantado corretamente em seu ambiente Elasticsearch antes de iniciar o armazenamento de vetores.</p><pre><code>elastic_vectorstore = ElasticsearchStore.from_documents(
    docs,
    es_url=ES_ENDPOINT,
    es_api_key=ES_API_KEY,
    index_name=index_name,
    strategy=SparseVectorStrategy(model_id=".elser_model_2"),
)

elastic_vectorstore.client.indices.refresh(index=index_name)</code></pre><p>A funcionalidade de busca na web é implementada usando <a href="https://python.langchain.com/api_reference/community/tools/langchain_community.tools.ddg_search.tool.DuckDuckGoSearchRun.html">o DuckDuckGoSearchRun</a> das ferramentas da comunidade LangChain, o que permite que o sistema recupere informações em tempo real da web de forma eficiente. Você também pode considerar o uso de outras APIs de busca que podem fornecer resultados mais relevantes. Essa ferramenta foi escolhida por permitir buscas sem a necessidade de uma chave de API.</p><pre><code>duckduckgo = DuckDuckGoSearchRun(description= "A custom DuckDuckGo search tool for finding latest news stories.", verbose=True)
def websearch_retriever(query):
    results = duckduckgo.run(f"{query}")
    return results</code></pre><p>O recuperador composto foi projetado para consultas que exigem uma combinação de fontes. É utilizado para fornecer uma resposta abrangente e contextualizada, recuperando simultaneamente dados em tempo real da web e consultando notícias históricas do banco de dados vetorial.</p><pre><code>def composite_retriever(query):
    related_docs = vectorstore_retriever(query)
    related_docs += websearch_retriever(query)
    return related_docs</code></pre><h3>Configurar os agentes</h3><p>Na etapa seguinte, os agentes LLM são definidos para fornecer capacidades de raciocínio e tomada de decisão dentro desse fluxo de trabalho. As cadeias LLM que criaremos incluem: <code>router_chain</code>, <code>grade_docs_chain</code>, <code>rewrite_query_chain</code> e <code>summary_chain</code>.</p><p>O agente de roteamento utiliza um assistente LLM para determinar a fonte de dados mais apropriada para uma determinada consulta em tempo de execução. O agente de classificação avalia os documentos recuperados quanto à sua relevância. Se os documentos forem considerados relevantes, eles são encaminhados ao agente de resumo para gerar um resumo. Caso contrário, o agente de reescrita de consultas reformula a consulta e a envia de volta ao processo de roteamento para uma nova tentativa de recuperação. Você pode encontrar as instruções para todos os agentes na seção Cadeias LLM do <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">caderno</a>.</p><pre><code>class RouteQuery(BaseModel):
    datasource: Literal["vectorstore", "websearch", "composite"] = Field(
        ...,
        description="Choose to route the query to web search, vectorstore or composite."
    )

router_prompt = ChatPromptTemplate.from_template("""You are an assistant that decides the best data source for questions based on news articles.
Choose one of the following options:
- 'vectorstore': for general, background, or historical news articles.
- 'websearch': for recent discoveries, 'latest', 'current', or '2025' type queries.
- 'composite': when the question needs both historical and current knowledge on news articles.

Question: {query}

Return one word: 'vectorstore', 'websearch', or 'composite'.
""")
router_structured = llm.with_structured_output(RouteQuery)
router_chain: RunnableSequence = router_prompt | router_structured</code></pre><p>O <code>llm.with_structured_output</code> restringe a saída do modelo a seguir um esquema predefinido definido pelo BaseModel sob a classe <code>RouteQuery</code> , garantindo a consistência dos resultados. A segunda linha compõe um <code>RunnableSequence</code> conectando <code>router_prompt</code> com <code>router_structured</code>, formando um pipeline no qual o prompt de entrada é processado pelo modelo de linguagem para produzir resultados estruturados e compatíveis com o esquema.</p><h3>Defina os nós do grafo.</h3><p>Esta parte envolve a definição dos estados do grafo, que representam os dados que fluem entre os diferentes componentes do sistema. Uma especificação clara desses estados garante que cada nó no fluxo de trabalho saiba quais informações ele pode acessar e atualizar.</p><pre><code>class RAGState(TypedDict):
    query: str
    docs: List[Document]
    router: str
    summary: str
    self_reflection: bool
    retry_count: int = 0</code></pre><p>Uma vez definidos os estados, o próximo passo é definir os nós do grafo. Os nós são como as unidades funcionais do grafo que executam operações específicas sobre os dados. Nosso pipeline possui 7 nós diferentes.</p><pre><code>def router(state: RAGState):
   router = router_chain.invoke({'query': state["query"]})
   logger.info(f"Router selected the datasource: {router.datasource}")
   logger.info(f"User query: {state['query']}")
   return {"router": router.datasource}

def vectorstore(state: RAGState):
   return {"docs": vectorstore_retriever(state["query"])}

def websearch(state: RAGState):
   return {"docs": websearch_retriever(state["query"])}

def composite(state: RAGState):
   return {"docs": composite_retriever(state["query"])}

def self_reflection(state: RAGState):
   evaluation = grade_docs_chain.invoke(
       {"query": state["query"], "docs": state["docs"]}
   )
   if evaluation.binary_score:
       logger.info(f"Self-reflection passed -- binary_score={evaluation.binary_score}")
   else:
       logger.info(f"Self-reflection failed -- binary_score={evaluation.binary_score}")

   return {
       "self_reflection": evaluation.binary_score,
   }

def query_rewriter(state: RAGState):
   retry_count = state.get("retry_count", 0) + 1
   new_query = rewrite_query_chain.invoke({"query": state["query"]})
   logger.info(f"Query rewritten: {new_query}, retry_count: {retry_count}")
   return {
       "query": new_query,
       "retry_count": retry_count,
   }

def summarize(state: RAGState):
   summary = summarize_chain.run(
       query=state["query"],
       docs=state["docs"],
   )
   return {"summary": summary}</code></pre><p>O nó <code>query_rewriter</code> serve a dois propósitos no fluxo de trabalho. Primeiro, ele reescreve a consulta do usuário usando o <code>rewrite_query_chain</code> para melhorar a recuperação quando os documentos avaliados pelo agente de autorreflexão são considerados insuficientes ou irrelevantes. Em segundo lugar, funciona como um contador que registra quantas vezes a consulta foi reescrita.</p><p>Cada vez que o nó é invocado, ele incrementa o <code>retry_count</code> armazenado no estado do fluxo de trabalho. Esse mecanismo impede que o fluxo de trabalho entre em um loop infinito. Se o <code>retry_count</code> exceder um limite predefinido, o sistema pode recorrer a um estado de erro, uma resposta padrão ou qualquer outra condição predefinida que você escolher.</p><h3>Compilando o gráfico</h3><p>O último passo é definir as arestas do grafo e adicionar quaisquer condições necessárias antes de compilá-lo. Cada grafo deve começar a partir de um nó inicial designado, que serve como ponto de entrada para o fluxo de trabalho. As arestas no grafo representam o fluxo de dados entre os nós e podem ser de dois tipos:</p><ul><li>Arestas retas: Estas definem um fluxo direto e incondicional de um nó para outro. Sempre que o primeiro nó conclui sua tarefa, o fluxo de trabalho avança automaticamente para o próximo nó ao longo da aresta reta.</li><li>Arestas condicionais: Permitem que o fluxo de trabalho se ramifique com base no estado atual ou nos resultados da computação de um nó. O próximo nó é selecionado dinamicamente, dependendo de condições como resultados de avaliação, decisões de roteamento ou número de tentativas.</li></ul><pre><code>graph.add_edge(START, "router")

def after_router(state: RAGState):
   route = state.get("router", None)
   if route == "vectorstore":
       return "vectorstore"
   elif route == "websearch":
       return "websearch"
   else:
       return "composite"

def after_self_reflection(state: RAGState):
   if state["self_reflection"]:
           return "summarize"
   return "query_rewriter"

def after_query_rewriter(state: RAGState):
   while state['retry_count'] <= 3:
           return "router"
   raise RuntimeError("Maximum retries (3) reached -- evaluation failed.")

graph.add_conditional_edges(
   "router",
   after_router,
   {
       "vectorstore": "vectorstore",
       "websearch": "websearch",
       "composite": "composite"
   }
)

graph.add_edge("vectorstore", "self_reflection")
graph.add_edge("websearch", "self_reflection")
graph.add_edge("composite", "self_reflection")
graph.add_conditional_edges(
   "self_reflection",
   after_self_reflection,
   {
       "summarize": "summarize",
       "query_rewriter": "query_rewriter"
   }
)
graph.add_conditional_edges("query_rewriter", after_query_rewriter, {"router": "router"})
graph.add_edge("summarize", END)
agent=graph.compile()</code></pre><p>Com isso, seu primeiro pipeline RAG agentivo está pronto e pode ser testado usando o agente compilado.</p><pre><code>result = agent.invoke({"query": query1})
logger.info(f"\nFinal Summary:\n: {result['summary']}")</code></pre><h3>Testando o pipeline RAG agentivo</h3><p>Agora vamos testar esse pipeline usando três tipos distintos de consultas, conforme descrito abaixo. Note que os resultados podem variar, e os exemplos mostrados abaixo ilustram apenas um resultado possível.</p><pre><code>query1="What are the latest AI models released this month?"
query2="What technological innovations are discussed in Sci/Tech news?"
query3="Compare a Sci/Tech article from the dataset with a current web article about AI trends."</code></pre><p>Para a primeira consulta, o roteador seleciona <code>websearch</code> como fonte de dados. A consulta falha na avaliação de autorreflexão e, consequentemente, é redirecionada para a etapa de reescrita da consulta, conforme mostrado na saída.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: What are the latest AI models released this month?
Latest Singapore news, including the city state's relationships with Malaysia and Mahathir, China and Xi Jinping, and the rest of Southeast Asia. 3 days ago · The latest military news, insights and analysis from China. All the latest news, opinions and analysis on Hong Kong, China, Asia and around the world Latest news, in-depth features and opinion on Malaysia, covering politics, economy, society and the Asean member-nation's relationships with China, Singapore, and other Southeast Asian ... Oct 12, 2025 · Brics (an acronym for Brazil, Russia, India, China and South Africa) refers to an association of 10 leading emerging markets. The other member states are Egypt, Ethiopia, ...
INFO     | __main__:self_reflection:31 - Self-reflection failed -- binary_score=False
INFO     | __main__:query_rewriter:40 - Query rewritten: query='Which AI models have been officially released in June 2024?', retry_count: 1
INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: query='Which AI models have been officially released in June 2024?'
Dream Machine is a text-to-video model created by Luma Labs and launched in June 2024 . It generates video output based on user prompts or still images. Dream Machine has been noted for its ability to realistically capture motion... Released in June 2023. In June 2024 , Baidu announced Ernie 4.0 Turbo. In April 2025, Ernie 4.5 Turbo and X1 Turbo were released . These models are optimized for faster response times and lower operational costs.[28][29]. The meaning of QUERY is question, inquiry. How to use query in a sentence. Synonym Discussion of Query. QUERY definition: 1. a question, often expressing doubt about something or looking for an answer from an authority.... Learn more. Query definition: a question; an inquiry.. See examples of QUERY used in a sentence.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: In June 2024, two AI models were officially released: Dream Machine, a text-to-video model launched by Luma Labs, and Ernie 4.0 Turbo, announced by Baidu, which is optimized for faster response times and lower operational costs.</code></pre><p>Em seguida, examinamos um exemplo onde a recuperação <code>vectorstore</code> é usada, demonstrado com a segunda consulta.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: vectorstore
INFO     | __main__:router:12 - User query: What technological innovations are discussed in Sci/Tech news?
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: Recent Sci/Tech news highlights several technological innovations: NASA is collaborating with Silicon Valley firms to build a powerful Linux-based supercomputer to support theoretical research and shuttle engineering; new chromatin transfer techniques have enabled the cloning of cats; cybersecurity advancements are being discussed in relation to protecting personal technology; Princeton University scientists assert that existing technologies can be used immediately to stabilize global warming; and a set of GameBoy micro-games has been recognized for innovation in game design.</code></pre><p>A consulta final é direcionada à recuperação composta, que utiliza tanto o armazenamento vetorial quanto a pesquisa na web.</p><pre><code>INFO     | __main__:router:11 - Router selected the datasource: composite
INFO     | __main__:router:12 - User query: Compare a Sci/Tech article from the dataset with a current web article about AI trends.
Atlas currently only available on macOS, built on Chromium with planned features like ad-blocking still in development. OpenAI's Atlas browser launched with bold promises of AI -powered web browsing, but early real-world testing reveals a different story. Career-long data are updated to end-of-2024 and single recent year data pertain to citations received during calendar year 2024. The selection is based on the top 100,000 scientists by c-score (with and without self-citations) or a percentile rank of 2% or above in the sub-field. In this article I list 45 AI tools across 21 different categories. After exploring all the available options in each category, I've carefully selected the best tools based on my personal experience. Reading a complex technical article ? Simply highlight confusing terminology and ask "what's this?" to receive instant explanations. compare browsers. Comparison showing traditional browser navigation versus OpenAI Atlas AI -powered workflows. After putting Gemini, ChatGPT, Grok, and DeepSeek through rigorous testing in October 2025, it's clear that there isn't one AI that reigns supreme across all categories.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:<module>:2 - 
Final Summary:
: A Sci/Tech article from the dataset highlights NASA's development of robust artificial intelligence software for planetary rovers, aiming to make them more self-reliant and capable of decision-making during missions. In contrast, a current web article about AI trends focuses on the proliferation of AI-powered tools across various categories, including browsers like OpenAI Atlas, and compares leading models such as Gemini, ChatGPT, Grok, and DeepSeek, noting that no single AI currently excels in all areas. While the NASA article emphasizes specialized AI applications for autonomous robotics in space exploration, the current trends article showcases the broadening impact of AI across consumer and professional technologies, with ongoing competition and rapid innovation among major AI platforms.</code></pre><p>No fluxo de trabalho acima, o RAG agente determina de forma inteligente qual fonte de dados usar ao recuperar informações para uma consulta do usuário, melhorando assim a precisão e a relevância da resposta. Você pode criar exemplos adicionais para testar o agente e analisar os resultados para verificar se eles produzem algum resultado interessante.</p><h2>Melhores práticas para a construção de fluxos de trabalho RAG com agentes</h2><p>Agora que entendemos como o RAG agético funciona, vamos analisar algumas práticas recomendadas para a construção desses fluxos de trabalho. Seguir estas diretrizes ajudará a manter o sistema eficiente e de fácil manutenção.</p><ul><li><strong>Prepare-se para planos de contingência</strong>: Planeje estratégias alternativas com antecedência para cenários em que qualquer etapa do fluxo de trabalho falhe. Isso pode incluir retornar respostas padrão, acionar estados de erro ou usar ferramentas alternativas. Isso garante que o sistema lide com as falhas de forma adequada, sem interromper o fluxo de trabalho geral.</li><li><strong>Implemente um registro abrangente</strong>: tente implementar o registro em cada etapa do fluxo de trabalho, como novas tentativas, saídas geradas, opções de roteamento e reescritas de consultas. Esses registros ajudam a melhorar a transparência, facilitam a depuração e auxiliam no aprimoramento de prompts, comportamento do agente e estratégias de recuperação ao longo do tempo.</li><li><strong>Selecione o padrão de fluxo de trabalho apropriado</strong>: Analise seu caso de uso e selecione o padrão de fluxo de trabalho que melhor atenda às suas necessidades. Utilize fluxos de trabalho sequenciais para raciocínio passo a passo, fluxos de trabalho paralelos para fontes de dados independentes e padrões de orquestrador-trabalhador para consultas complexas ou que envolvam múltiplas ferramentas.</li><li><strong>Incorporar estratégias de avaliação</strong>: Integrar mecanismos de avaliação em diferentes etapas do fluxo de trabalho. Isso pode incluir agentes de autorreflexão, classificação de documentos recuperados ou verificações de qualidade automatizadas. A avaliação ajuda a verificar se os documentos recuperados são relevantes, se as respostas são precisas e se todas as partes de uma consulta complexa foram abordadas.</li></ul><h2>Desafios</h2><p>Embora os sistemas RAG agentivos ofereçam vantagens significativas em termos de adaptabilidade, precisão e raciocínio dinâmico, eles também apresentam certos desafios que devem ser abordados durante as fases de projeto e implementação. Alguns dos principais desafios incluem:</p><ul><li><strong>Fluxos de trabalho complexos</strong>: À medida que mais agentes e pontos de decisão são adicionados, o fluxo de trabalho geral torna-se cada vez mais complexo. Isso pode levar a uma maior probabilidade de erros ou falhas em tempo de execução. Sempre que possível, priorize fluxos de trabalho simplificados, eliminando agentes redundantes e pontos de decisão desnecessários.</li><li><strong>Escalabilidade</strong>: Pode ser desafiador dimensionar sistemas RAG com agentes para lidar com grandes conjuntos de dados e altos volumes de consultas. Incorpore estratégias eficientes de indexação, armazenamento em cache e processamento distribuído para manter o desempenho em grande escala.</li><li><strong>Orquestração e sobrecarga computacional</strong>: A execução de fluxos de trabalho com múltiplos agentes requer orquestração avançada. Isso inclui um planejamento cuidadoso, gerenciamento de dependências e coordenação de agentes para evitar gargalos e conflitos, fatores que contribuem para a complexidade geral do sistema.</li><li><strong>Complexidade da avaliação</strong>: A avaliação desses fluxos de trabalho apresenta desafios inerentes, uma vez que cada etapa requer uma estratégia de avaliação distinta. Por exemplo, a etapa RAG deve ser avaliada quanto à relevância e completude dos documentos recuperados, enquanto os resumos gerados precisam ser verificados quanto à qualidade e precisão. Da mesma forma, a eficácia da reformulação de consultas requer uma lógica de avaliação separada para determinar se a consulta reescrita melhora os resultados da recuperação.</li></ul><h2>Conclusão</h2><p>Neste post do blog, apresentamos o conceito de RAG agente e destacamos como ele aprimora a estrutura tradicional de RAG, incorporando capacidades autônomas da IA agente. Exploramos as principais funcionalidades do RAG agentivo e demonstramos essas funcionalidades por meio de um exemplo prático, construindo um assistente de notícias usando o Elasticsearch como repositório de vetores e o LangChain para criar a estrutura agentiva.</p><p>Além disso, discutimos as melhores práticas e os principais desafios a serem considerados ao projetar e implementar um pipeline RAG com agentes. Essas informações têm como objetivo orientar os desenvolvedores na criação de sistemas de agentes robustos, escaláveis e eficientes que combinem efetivamente recuperação de dados, raciocínio e tomada de decisões.</p><h2>O que vem a seguir</h2><p>O fluxo de trabalho que desenvolvemos é simples, deixando bastante espaço para melhorias e experimentação. Podemos melhorar isso experimentando com vários modelos de incorporação e refinando as estratégias de recuperação. Além disso, a integração de um agente de reclassificação para priorizar os documentos recuperados pode ser benéfica. Outra área a ser explorada envolve o desenvolvimento de estratégias de avaliação para estruturas de agentes, especificamente a identificação de abordagens comuns e reutilizáveis aplicáveis a diferentes tipos de estruturas. Por fim, experimentar essas estruturas em conjuntos de dados grandes e mais complexos.</p><p>Entretanto, se você tiver experiências semelhantes para compartilhar, adoraríamos saber mais sobre elas! Fique à vontade para enviar seus comentários ou entrar em contato conosco por meio do nosso <a href="https://ela.st/slack">canal da comunidade no Slack</a> ou <a href="https://discuss.elastic.co/c/security">dos fóruns de discussão</a>.</p><h2>Recursos</h2><ul><li><a href="https://arxiv.org/abs/2310.11511">Auto-RAG: Aprendendo a Recuperar, Gerar e Criticar por meio da Autorreflexão</a></li><li><a href="https://arxiv.org/abs/2501.09136">Geração Aumentada por Recuperação Agencial: Uma Análise sobre RAG Agencial</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Busca híbrida sem complicações: simplificando a busca híbrida com recuperadores.]]></title>
            <description><![CDATA[Descubra como simplificar a busca híbrida no Elasticsearch com um formato de consulta de múltiplos campos para recuperadores lineares e RRF, e crie consultas sem conhecimento prévio sobre seu índice do Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/hybrid-search-multi-field-query-retrievers-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Mike Pellegrini]]></dc:creator>
            <pubDate>Thu, 27 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4494f98ae3958bbdbc6171df9677fc4d65ec5640-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/what-is/hybrid-search">A busca híbrida</a> é amplamente reconhecida como uma abordagem de busca poderosa, combinando a precisão e a velocidade da <a href="https://www.elastic.co/search-labs/blog/lexical-and-semantic-search-with-elasticsearch#lexical-search---sparse-retrieval">busca lexical</a> com os recursos de linguagem natural da <a href="https://www.elastic.co/what-is/semantic-search">busca semântica</a>. No entanto, aplicá-lo na prática pode ser complicado, muitas vezes exigindo conhecimento profundo sobre o índice e a construção de consultas verbosas com configurações complexas. Neste blog, exploraremos como o <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">formato de consulta com múltiplos campos para buscadores lineares e RRF</a> torna a busca híbrida mais simples e acessível, eliminando problemas comuns e permitindo que você aproveite todo o seu potencial com maior facilidade. Analisaremos também como o formato de consulta com vários campos permite realizar consultas de pesquisa híbridas sem conhecimento prévio sobre o índice.</p><h2>O problema da amplitude de pontuação</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c8b49153c47d2cdc233c0d2e440db04711d48ca5-1600x1600.jpg" style="width: 100%;" /><p>Para contextualizar, vamos analisar um dos principais motivos pelos quais a busca híbrida pode ser difícil: a variação nos intervalos de pontuação. Nosso velho amigo <a href="https://www.elastic.co/elasticon/conf/2016/sf/improved-text-scoring-with-bm25">BM25</a> produz pontuações ilimitadas. Em outras palavras, o BM25 pode gerar pontuações que variam de perto de 0 até (teoricamente) o infinito. Em contraste, as consultas aos campos <code>dense_vector</code> produzirão pontuações limitadas entre 0 e 1. Exacerbando este problema, <code>semantic_text</code> ofusca o tipo de campo usado para indexar embeddings, portanto, a menos que você tenha conhecimento detalhado sobre a configuração do seu índice e endpoint de inferência, pode ser difícil dizer qual será o intervalo de pontuação da sua consulta. Isso representa um problema ao tentar intercalar resultados de busca lexical e semântica, já que os resultados lexicais podem ter precedência sobre os semânticos, mesmo que os resultados semânticos sejam mais relevantes. A solução geralmente aceita para esse problema é normalizar as pontuações antes de intercalar os resultados. O Elasticsearch possui duas ferramentas para isso: os recuperadores <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/linear-retriever">lineares</a> e <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rrf-retriever">RRF</a> .<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/264481268c8b6ac259e3c257b85431b513f16672-1077x586.png" style="width: 100%;" /><p>O recuperador <strong>RRF</strong> aplica o <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">algoritmo RRF</a>, usando a classificação do documento como medida de relevância e descartando a pontuação. Como a pontuação não é considerada, as discrepâncias na faixa de pontuação não representam um problema.</p><p>O recuperador <strong>linear</strong> utiliza uma combinação linear para determinar a pontuação final de um documento. Isso envolve pegar a pontuação de cada consulta de componente para o documento, normalizá-la e somá-las para gerar a pontuação total. Matematicamente, a operação pode ser expressa como:</p><pre><code>Total Score = 𝚺(N(Sx))</code></pre><p>Onde <code>N</code> é a função de normalização e SX é a pontuação para a consulta X. A função de normalização é fundamental aqui, pois transforma a pontuação de cada consulta para usar o mesmo intervalo. Você pode aprender mais sobre o recuperador linear <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">aqui</a>.</p><h2>Analisando detalhadamente</h2><p>Os usuários podem implementar uma busca híbrida eficaz com essas ferramentas, mas isso requer algum conhecimento sobre o seu índice. Vejamos um exemplo com o recuperador linear, onde consultaremos um índice com dois campos:</p><pre><code>PUT linear_retriever_example
{
  "mappings": {
    "properties": {
      "semantic_text_field": { <1>
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elasticsearch"
      },
      "text_field": { <2>
        "type": "text"
      }
    }
  }
}</code></pre><p>1. <code>semantic_text_field</code> é um campo <code>semantic_text</code> que usa <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-e5">E5</a>, um modelo de incorporação de texto.</p><p>2. <code>text_field</code> é um campo <code>text</code> padrão</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "match": { <1>
                  "semantic_text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>1. Usamos uma consulta <code>match</code> em nosso campo <code>semantic_text</code> , para o qual <a href="https://www.elastic.co/search-labs/blog/semantic-search-match-knn-sparse-vector#we-made-match-happen-in-semantic-search!">adicionamos suporte no Elasticsearch 8.18/9.0</a></p><p><br/>Ao construir a consulta, precisamos ter em mente que <code>semantic_text_field</code> usa um modelo de incorporação de texto, portanto, quaisquer consultas sobre ele gerarão uma pontuação entre 0 e 1. Precisamos também saber que <code>text_field</code> é um campo <code>text</code> padrão e, portanto, as consultas nele gerarão uma pontuação ilimitada. Para criar um conjunto de resultados com a relevância adequada, precisamos usar um mecanismo de recuperação que normalize as pontuações das consultas antes de combiná-las. Neste exemplo, usamos o recuperador linear com normalização <code>minmax</code> , que normaliza a pontuação de cada consulta para um valor entre 0 e 1.</p><p>A construção da consulta neste exemplo é bastante simples, pois envolve apenas dois campos. No entanto, a situação pode se complicar rapidamente à medida que mais campos, e de tipos variados, são adicionados. Isso demonstra como escrever uma consulta de pesquisa híbrida eficaz geralmente requer um conhecimento mais profundo do índice consultado, para que as pontuações das consultas componentes sejam devidamente normalizadas antes da combinação. Isso representa uma barreira para a adoção mais ampla da busca híbrida.</p><h3>Agrupamento de consultas</h3><p>Vamos expandir o exemplo: E se quiséssemos consultar um campo <code>text</code> e dois campos <code>semantic_text</code> ? Poderíamos construir uma consulta como esta:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_1",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "semantic": {
                  "field": "semantic_text_field_2",
                  "query": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>Isso parece bom à primeira vista, mas existe um problema em potencial. Agora, as correspondências do campo <code>semantic_text</code> representam ⅔ da pontuação total:</p><pre><code>Total Score = N(semantic_text_field_1 score) + N(semantic_text_field_2 score) + N(text_field score)</code></pre><p>Provavelmente não é isso que você deseja, pois cria uma pontuação desequilibrada. Os efeitos podem não ser tão perceptíveis em um exemplo como este, com apenas 3 campos, mas tornam-se problemáticos quando mais campos são consultados. Por exemplo, a maioria dos índices contém muito mais campos lexicais do que semânticos (ou seja, <code>dense_vector</code>, <code>sparse_vector</code> ou <code>semantic_text</code>). E se estivéssemos consultando um índice com 9 campos lexicais e 1 campo semântico usando o padrão acima? As correspondências lexicais representariam 90% da pontuação, diminuindo a eficácia da busca semântica.</p><p>Uma forma comum de resolver isso é agrupar as consultas em categorias lexicais e semânticas e atribuir pesos iguais a ambas. Isso impede que qualquer uma das categorias domine a pontuação total.</p><p>Vamos colocar isso em prática. Como seria essa abordagem de consultas agrupadas neste exemplo ao usar o recuperador linear?</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "retrievers": [
        {
          "retriever": {
            "linear": {
              "retrievers": [
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_1",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                },
                {
                  "retriever": {
                    "standard": {
                      "query": {
                        "semantic": {
                          "field": "semantic_text_field_2",
                          "query": "foo"
                        }
                      }
                    }
                  },
                  "normalizer": "minmax"
                }
              ]
            }
          },
          "normalizer": "minmax"
        },
        {
          "retriever": {
            "standard": {
              "query": {
                "match": {
                  "text_field": "foo"
                }
              }
            }
          },
          "normalizer": "minmax"
        }
      ]
    }
  }
}</code></pre><p>Uau, isso está ficando prolixo! Você pode até ter precisado rolar a página para cima e para baixo várias vezes para examinar toda a consulta! Aqui, utilizamos dois níveis de normalização para criar os grupos de consulta. Matematicamente, pode ser expresso como:</p><pre><code>Total Score = N(N(semantic_text_field_1 score) + N(semantic_text_field_2 score)) + N(text_field score)</code></pre><p>Este segundo nível de normalização garante que as consultas aos campos <code>semantic_text</code> e <code>text</code> sejam ponderadas igualmente. Observe que omitimos a normalização de segundo nível para <code>text_field</code> neste exemplo, uma vez que há apenas um campo lexical, poupando-o de <em>ainda mais</em> verbosidade.</p><p>Essa estrutura de consulta já é complexa demais, e estamos consultando apenas três campos. À medida que se consultam mais campos, a tarefa torna-se cada vez mais difícil de gerir, mesmo para profissionais de pesquisa experientes.</p><h2>O formato de consulta com vários campos</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dd04e1686076a574b717c1460acfe4eb79299208-1600x1600.jpg" style="width: 100%;" /><p>Adicionamos o <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers#multi-field-query-format">formato de consulta com vários campos</a> para os recuperadores lineares e RRF no Elasticsearch 8.19, 9.1 e <a href="https://www.elastic.co/cloud/serverless">serverless</a> para simplificar tudo isso. Agora você pode realizar a mesma consulta acima apenas com:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>O que reduz a consulta de 55 linhas para apenas 9! O Elasticsearch usa automaticamente os mapeamentos de índice para:</p><ul><li>Determine o tipo de cada campo consultado.</li><li>Agrupe cada campo em uma categoria lexical ou semântica.</li><li>Dê o mesmo peso a cada categoria na pontuação final.</li></ul><p>Isso permite que qualquer pessoa execute uma consulta de pesquisa híbrida eficaz sem precisar saber detalhes sobre o índice ou os endpoints de inferência utilizados.</p><p>Ao usar o RRF, você pode omitir o <code>normalizer</code>, já que a classificação é usada como um indicador de relevância:</p><pre><code>GET rrf_retriever_example/_search
{
  "retriever": {
    "rrf": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field" ],
      "query": "foo"
    }
  }
}</code></pre><h2>Aumento por campo</h2><p>Ao usar o recuperador linear, você pode aplicar um reforço por campo para ajustar a importância das correspondências em determinados campos. Por exemplo, digamos que você esteja consultando quatro campos: dois campos <code>semantic_text</code> e dois campos <code>text</code> :</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1", "semantic_text_field_2", "text_field_1", "text_field_2" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Por padrão, cada campo tem o mesmo peso em seu grupo (lexical ou semântico). A distribuição da pontuação é a seguinte:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ba31c76189e3a1e5b1638437ccf0528aafec2598-1600x549.png" style="width: 100%;" /><p>Em outras palavras, cada área corresponde a 25% da pontuação total.</p><p>Podemos usar a sintaxe <code>field^boost</code> para adicionar um aumento por campo a qualquer campo. Vamos aplicar um aumento de 2 a <code>semantic_text_field_1</code> e <code>text_field_1</code>:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_1^2", "semantic_text_field_2", "text_field_1^2", "text_field_2" ]
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Agora a distribuição da pontuação é a seguinte:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/de20e555d52f914bf483a048d056f54f4fece757-1600x549.png" style="width: 100%;" /><p>Cada grupo de consultas ainda tem o mesmo peso, mas agora o peso dos campos dentro dos grupos foi alterado:</p><ul><li><code>semantic_text_field_1</code> representa 66% da pontuação do grupo de consultas semânticas e 33% da pontuação total.</li><li><code>text_field_1</code> representa 66% da pontuação do grupo de consulta lexical e 33% da pontuação total.</li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">ℹ️ Observe que o intervalo de pontuação total não será alterado quando um aumento por campo for aplicado. Este é um efeito colateral intencional da normalização de pontuação, que garante que as pontuações das consultas lexicais e semânticas permaneçam diretamente comparáveis entre si.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">ℹ️ O reforço por campo também pode ser usado com o recuperador RRF no Elasticsearch 9.2+</td>
                    </tr>
              </tbody>
            </table>
          <h3>Resolução curinga</h3><p>Você pode usar o caractere curinga <code>*</code> no parâmetro <code>fields</code> para corresponder a vários campos. Continuando o exemplo acima, esta consulta é funcionalmente equivalente a consultar explicitamente s<code>emantic_text_field_1</code>, <code>semantic_text_field_2</code> e <code>text_field_1</code> :</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "fields": [ "semantic_text_field_*", "*_field_1" ],
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>É interessante notar que o padrão <code>*_field_1</code> corresponde tanto <code>text_field_1</code> quanto a <code>semantic_text_field_1</code>. Isso é tratado automaticamente; a consulta será executada como se cada um dos campos tivesse sido consultado explicitamente. Também não há problema em que <code>semantic_text_field_1</code> corresponda a ambos os padrões; todas as correspondências de nomes de campos são desduplicadas antes da execução da consulta.</p><p>Você pode usar o caractere curinga de diversas maneiras:</p><ul><li>Correspondência de prefixo (ex: <code>*_text_field</code>)</li><li>Correspondência em linha (ex: <code>semantic_*_field</code>)</li><li>Correspondência de sufixo (ex: <code>semantic_text_field_*</code>)</li></ul><p>Você também pode usar vários curingas para aplicar uma combinação do acima, como <code>*_text_field_*</code>.</p><h3>Campos de consulta padrão</h3><p>O formato de consulta com vários campos também permite consultar um índice sobre o qual você não sabe nada. Se você omitir o parâmetro <code>fields</code> , a consulta abrangerá todos os campos especificados pela <a href="https://www.elastic.co/docs/reference/elasticsearch/index-settings/index-modules">configuração de índice index.query.default_field</a>:</p><pre><code>GET linear_retriever_example/_search
{
  "retriever": {
    "linear": {
      "query": "foo",
      "normalizer": "minmax"
    }
  }
}</code></pre><p>Por padrão, <code>index.query.default_field</code> é definido como <code>*</code>. Este caractere curinga será resolvido para todos os tipos de campo no índice que suportam consultas por termo, que são a maioria. As exceções são:</p><ul><li><code>dense_vector</code> campos</li><li><code>rank_vector</code> campos</li><li>Campos geométricos: <code>geo_point</code>, <code>shape</code></li></ul><p>Essa funcionalidade é especialmente útil quando você deseja realizar uma consulta de pesquisa híbrida em um índice fornecido por terceiros. O formato de consulta com vários campos permite executar uma consulta adequada de forma simples. Basta excluir o parâmetro <code>fields</code> e todos os campos aplicáveis serão consultados.</p><h2>Conclusão</h2><p>O problema do intervalo de pontuação pode tornar a implementação de uma busca híbrida eficaz bastante complexa, especialmente quando há pouca informação sobre o índice consultado ou os endpoints de inferência em uso. O formato de consulta com múltiplos campos para os mecanismos de recuperação linear e RRF atenua esse problema, integrando uma abordagem de busca híbrida automatizada, baseada em agrupamento de consultas, em uma API simples e acessível. Funcionalidades adicionais, como reforço por campo, resolução de curingas e campos de consulta padrão, ampliam a funcionalidade para abranger diversos casos de uso.</p><h2>Experimente o formato de consulta com vários campos hoje mesmo.</h2><p>Você pode conferir os mecanismos de recuperação linear e RRF com o formato de consulta de múltiplos campos em projetos Elasticsearch <a href="https://www.elastic.co/cloud/serverless">Serverless</a> totalmente gerenciados, com um <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">período de avaliação gratuito</a>. Também está disponível em versões de pilha a partir das versões 8.19 e 9.1.</p><p>Comece em minutos no seu ambiente local com um único comando:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando um agente de IA para RH com Elastic Agent Builder e GPT-OSS]]></title>
            <description><![CDATA[Descubra como criar um agente de IA capaz de responder a consultas em linguagem natural sobre os dados de RH dos seus funcionários usando o Elastic Agent Builder e o GPT-OSS.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
            <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Introdução</h2><p>Este artigo mostrará como criar um agente de IA para RH usando <a href="https://openai.com/index/introducing-gpt-oss/">GPT-OSS</a> e Elastic Agent Builder. O agente pode responder às suas perguntas sem enviar dados para a OpenAI, Anthropic ou qualquer serviço externo.</p><p>Usaremos o LM Studio para disponibilizar o GPT-OSS localmente e conectá-lo ao Elastic Agent Builder.</p><p>Ao final deste artigo, você terá um agente de IA personalizado capaz de responder a perguntas em linguagem natural sobre os dados de seus funcionários, mantendo o controle total sobre suas informações e modelo.</p><h2>Pré-requisitos</h2><p>Para ler este artigo, você precisa de:</p><ul><li><a href="https://www.elastic.co/cloud">Elastic Cloud</a> hospedado na versão 9.2, implantação <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">local</a> ou sem servidor.</li><li>Recomenda-se máquina com 32 GB de RAM (mínimo de 16 GB para GPT-OSS 20B).</li><li><a href="https://lmstudio.ai/">LM Studio</a> instalado</li><li><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a> instalado</li></ul><h2>Por que usar GPT-OSS?</h2><p>Com um LLM local, você tem o controle para implantá-lo em sua própria infraestrutura e ajustá-lo para atender às suas necessidades específicas. Tudo isso mantendo o controle sobre os dados que você compartilha com o modelo e, claro, sem precisar pagar nenhuma taxa de licença a um fornecedor externo.</p><p>A OpenAI <a href="https://openai.com/index/introducing-gpt-oss/">lançou o GPT-OSS</a> em 5 de agosto de 2025, como parte de seu compromisso com o ecossistema de modelos abertos.</p><p>O modelo de parâmetros 20B oferece:</p><ul><li><strong>capacidades de utilização da ferramenta</strong></li><li><strong>Inferência eficiente</strong></li><li><strong>Compatível com o SDK OpenAI</strong></li><li><strong>Compatível com fluxos de trabalho agentes</strong></li></ul><p>Comparação de referência:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/29160e3345352088e8213297630882f252b00c47-1600x680.png" style="width: 100%;" /><h2>Arquitetura da solução</h2><p>A arquitetura é executada inteiramente em sua máquina local. O Elastic (executado em um contêiner Docker) se comunica diretamente com seu LLM local por meio do LM Studio, e o Elastic Agent Builder usa essa conexão para criar agentes de IA personalizados que podem consultar os dados de seus funcionários.</p><p>Para obter mais detalhes, consulte esta <a href="https://www.elastic.co/docs/solutions/observability/connect-to-own-local-llm">documentação</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" style="width: 100%;" /><h2>Construindo um agente de IA para RH: Etapas</h2><p>Dividiremos a implementação em 5 etapas:</p><ol><li>Configure o LM Studio com um modelo local.</li><li>Implante o Elastic local com o Docker.</li><li>Crie o conector OpenAI no Elastic</li><li>Carregar dados de funcionários no Elasticsearch</li><li>Crie e teste seu agente de IA.</li></ol><h2>Etapa 1: Configurar o LM Studio com GPT-OSS 20B</h2><p>O LM Studio é um aplicativo fácil de usar que permite executar grandes modelos de linguagem localmente em seu computador. Ele fornece um servidor de API compatível com OpenAI, facilitando a integração com ferramentas como o Elastic, sem um processo de configuração complexo. Para obter mais detalhes, consulte a <a href="https://lmstudio.ai/docs/app">documentação do LM Studio</a>.</p><p>Primeiro, baixe e instale <span class="unknown__pt__mark__51566e628851">o LM Studio</span> a partir do site oficial. Após a instalação, abra o aplicativo.</p><h3>Na interface do LM Studio:</h3><ol><li>Acesse a aba de pesquisa e procure por “GPT-OSS”.</li><li>Selecione o <code>openai/gpt-oss-20b</code> da OpenAI</li><li>Clique em baixar</li></ol><p>O tamanho deste modelo deverá ser de aproximadamente <strong>12,10 GB</strong>. O download pode demorar alguns minutos, dependendo da sua conexão com a internet.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" style="width: 100%;" /><h4>Após o download do modelo:</h4><ol><li>Acesse a aba do servidor local.</li><li>Selecione o openai/gpt-oss-20b</li><li>Use a porta padrão 1234</li><li>No painel direito, acesse <strong>Carregar </strong>e defina o Comprimento do Contexto para <strong>40K</strong> ou mais.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" style="width: 100%;" /><p>5. Clique em Iniciar servidor</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" style="width: 100%;" /><p>Você deverá ver isso se o servidor estiver em execução.</p><pre><code>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] ->	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] ->	POST http://localhost:1234/v1/embeddings
Server started.</code></pre><h2>Etapa 2: Implante o Elastic local com o Docker</h2><p>Agora vamos configurar o Elasticsearch e o Kibana localmente usando o Docker. A Elastic fornece um script prático que lida com todo o processo de configuração. Para obter mais detalhes, consulte a <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">documentação oficial</a>.</p><h3>Execute o script start-local</h3><p>Execute o seguinte comando no seu terminal:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre><p>Este script irá:</p><ul><li>Baixe e configure o Elasticsearch e o Kibana.</li><li>Inicie ambos os serviços usando o Docker Compose.</li><li>Ative automaticamente uma licença de avaliação Platinum de 30 dias.</li></ul><h3>Resultado esperado</h3><p>Aguarde a seguinte mensagem e salve a senha e a chave da API exibidas; você precisará delas para acessar o Kibana:</p><pre><code>🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: KSUlOMNr
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: cnJGX0pwb0JhOG00cmNJVklUNXg6cnNJdXZWMnM4bncwMllpQlFlUTlWdw==
Learn more at https://github.com/elastic/start-local</code></pre><h3>Acesse o Kibana</h3><p>Abra seu navegador e acesse:</p><pre><code>http://localhost:5601</code></pre><p>Faça login utilizando as credenciais obtidas na saída do terminal.</p><h3>Habilitar o Construtor de Agentes</h3><p>Após fazer login no Kibana, navegue até <strong>Gerenciamento </strong>&gt;<strong> IA </strong>&gt;<strong> Construtor de Agentes </strong>e ative o Construtor de Agentes.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" style="width: 100%;" /><h2>Etapa 3: Crie o conector OpenAI no Elastic</h2><p>Agora vamos configurar o Elastic para usar seu LLM local.</p><h3>Conectores de acesso</h3><ol><li>Em Kibana</li><li>Acesse <strong>Configurações do projeto</strong> &gt; <strong>Gerenciamento</strong></li><li>Em <strong>Alertas e insights</strong>, selecione <strong>Conectores.</strong></li><li>Clique em Criar conector</li></ol><h3>Configure o conector</h3><p>Selecione <strong>OpenAI</strong> na lista de conectores. O LM Studio utiliza o SDK da OpenAI, o que o torna compatível.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" style="width: 100%;" /><p>Preencha os campos com estes valores:</p><ul><li><strong>Nome do conector: </strong>LM Studio - GPT-OSS 20B</li><li><strong>Selecione um provedor OpenAI: </strong>Outro (Serviço compatível com OpenAI)</li><li><strong>URL: </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></li><li><strong>Modelo padrão: </strong>openai/gpt-oss-20b</li><li><strong>Chave da API:</strong> testkey-123 (qualquer texto funciona, pois o LM Studio Server não requer autenticação).</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" style="width: 100%;" /><p>Para finalizar a configuração, clique em <strong>Salvar e testar</strong>.</p><p><strong>Importante:</strong> Ative a opção “<strong>Habilitar chamada de função nativa</strong>”; isso é necessário para que o Construtor de Agentes funcione corretamente. Se você não habilitar isso, você receberá um erro <strong><code>No tool calls found in the response</code></strong> .</p><h3>Teste a conexão</h3><p>O Elastic deve testar a conexão automaticamente. Se tudo estiver configurado corretamente, você verá uma mensagem de sucesso como esta:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" style="width: 100%;" /><p>Resposta.</p><pre><code>{
  "status": "ok",
  "data": {
    "id": "chatcmpl-flj9h0hy4wcx4bfson00an",
    "object": "chat.completion",
    "created": 1761189456,
    "model": "openai/gpt-oss-20b",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "Hello! 👋 How can I assist you today?",
          "reasoning": "Just greet.",
          "tool_calls": []
        },
        "logprobs": null,
        "finish_reason": "stop"
      }
    ],
    "usage": {
      "prompt_tokens": 69,
      "completion_tokens": 23,
      "total_tokens": 92
    },
    "stats": {},
    "system_fingerprint": "openai/gpt-oss-20b"
  },
  "actionId": "ee1c3aaf-bad0-4ada-8149-118f52dad757"
}</code></pre><h2>Etapa 4: Carregar os dados dos funcionários no Elasticsearch</h2><p>Agora vamos carregar o <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">conjunto de dados de funcionários de RH</a> para demonstrar como o agente trabalha com dados confidenciais. Eu gerei um conjunto de dados fictício com essa estrutura.</p><h3>Estrutura do conjunto de dados</h3><pre><code>{
  "employee_id": "0f4dce68-2a09-4cb1-b2af-6bcb4821539b",
  "full_name": "Daffi Stiebler",
  "email": "lscutchings0@huffingtonpost.com",
  "date_of_birth": "1975-06-20T15:39:36Z",
  "hire_date": "2025-07-28T00:10:45Z",
  "job_title": "Physical Therapy Assistant",
  "department": "HR",
  "salary": "108455",
  "performance_rating": "Needs Improvement",
  "years_of_experience": 2,
  "skills": "Java",
  "education_level": "Master's Degree",
  "manager": "Carl MacGibbon",
  "emergency_contact": "Leigha Scutchings",
  "home_address": "5571 6th Park"
}</code></pre><h3>Criar o índice com mapeamentos</h3><p>Primeiro, crie o índice com os mapeamentos adequados. Observe que estamos usando campos <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> para alguns campos-chave; isso possibilita recursos de busca semântica em nosso índice.</p><pre><code>​​PUT hr-employees
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "employee_id": {
        "type": "keyword"
      },
      "full_name": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "email": {
        "type": "keyword"
      },
      "date_of_birth": {
        "type": "date",
        "format": "iso8601"
      },
      "hire_date": {
        "type": "date",
        "format": "iso8601"
      },
      "job_title": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "department": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "salary": {
        "type": "double"
      },
      "performance_rating": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "years_of_experience": {
        "type": "long"
      },
      "skills": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "education_level": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "manager": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "emergency_contact": {
        "type": "keyword"
      },
      "home_address": {
        "type": "keyword"
      },
      "employee_semantic": {
        "type": "semantic_text"
      }
    }
  }
}</code></pre><h3>Indexar com API em lote</h3><p>Copie e cole o <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">conjunto de dados</a> nas suas Ferramentas de Desenvolvedor no Kibana e execute-o:</p><pre><code>POST hr-employees/_bulk
{"index": {}}
{"employee_id": "57728b91-e5d7-4fa8-954a-2384040d3886", "full_name": "Filide Gane", "email": "vhallahan1@booking.com", "job_title": "Business Systems Development Analyst", "department": "Marketing", "salary": "$52330.27", "performance_rating": "Meets Expectations", "years_of_experience": 12, "skills": "Java", "education_level": "Bachelor's Degree", "date_of_birth": "2000-02-07T16:49:32Z", "hire_date": "2023-11-07T13:03:16Z", "manager": "Freedman Kings", "emergency_contact": "Vilhelmina Hallahan", "home_address": "75 Dennis Junction"}
{"index": {}}
{"employee_id": "...", ...}</code></pre><h3>Verifique os dados</h3><p>Execute uma consulta para verificar:</p><pre><code>GET hr-employees/_search</code></pre><h2>Etapa 5: Crie e teste seu agente de IA</h2><p>Com tudo configurado, é hora de criar um agente de IA personalizado usando o Elastic Agent Builder. Para obter mais detalhes, consulte a <a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">documentação da Elastic</a>.</p><h3>Adicione o conector</h3><p>Antes de podermos criar nosso novo agente, precisamos configurar nosso construtor de agentes para usar nosso conector personalizado chamado <code>LM Studio - GPT-OSS 20B</code> , porque o padrão é o <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a>. Para isso, precisamos acessar <strong>Configurações do Projeto</strong> &gt; <strong>Gerenciamento</strong> &gt; <strong>Configurações do GenAI</strong>; agora selecionamos a que criamos e clicamos em <strong>Salvar</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" style="width: 100%;" /><h3>Construtor de Agentes de Acesso</h3><ol><li>Acesse a seção de <strong>Agentes.</strong></li><li>Clique em <strong>Criar um novo agente</strong></li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" style="width: 100%;" /><h3>Configure o agente</h3><p>Para criar um novo agente, os campos obrigatórios são o <strong>ID do Agente</strong>, <strong>o Nome de Exibição</strong> e <strong>as Instruções de Exibição</strong>.</p><p>Mas existem mais opções de personalização, como as Instruções Personalizadas, que orientam o comportamento do seu agente e a forma como ele interagirá com as suas ferramentas, de forma semelhante a um prompt do sistema, mas para o nosso agente personalizado. As etiquetas ajudam a organizar seus agentes, a cor do avatar e o símbolo do avatar.</p><p>Os agentes que escolhi para o nosso agente, com base no conjunto de dados, são:<br/><br/><strong>ID do agente:</strong> <code>hr_assistant</code></p><p><strong>Instruções personalizadas:</strong></p><pre><code>You are an HR Analytics Assistant that helps answer questions about employee data.
When responding to queries:
- Provide clear, concise answers
- Include relevant employee details (name, department, salary, skills)
- Format monetary values with currency symbols
- Be professional and maintain data confidentiality</code></pre><p><br/>Rótulos: <code>Human Resources</code> e <code>GPT-OSS</code></p><p>Nome de exibição: <code>HR Analytics Assistant</code></p><p>Descrição da tela:</p><pre><code>A specialized AI assistant for Human Resources that helps analyze employee data, compensation, performance metrics, and talent management. Ask questions about employees, departments, salaries, or performance analytics.</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" style="width: 100%;" /><p>Com todos os dados inseridos, podemos clicar em <strong>Salvar</strong> nosso novo agente.</p><h3>Teste o agente</h3><p>Agora você pode fazer perguntas em linguagem natural sobre os dados de seus funcionários, e o GPT-OSS 20B entenderá a intenção e gerará uma resposta apropriada.</p><h4>Incitar:</h4><pre><code>Which employee is the one with the highest salary in the hr-employees index?</code></pre><h4>Responder:</h4><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" style="width: 100%;" /><p>O processo do Agente foi o seguinte:</p><p>1. Compreenda sua pergunta usando o conector GPT-OSS.</p><p>2. Gere a consulta Elasticsearch apropriada (usando as ferramentas integradas ou <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> personalizado).</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" style="width: 100%;" /><p>3. Recuperar registros de funcionários correspondentes</p><p>4. Apresentar os resultados em linguagem natural com formatação adequada.</p><p>Diferentemente da busca lexical tradicional, o agente baseado em GPT-OSS entende a intenção e o contexto, facilitando a localização de informações sem a necessidade de conhecer os nomes exatos dos campos ou a sintaxe da consulta. Para obter mais detalhes sobre o processo de pensamento do agente, consulte este <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance">artigo</a>.</p><h2>Conclusão</h2><p>Neste artigo, criamos um agente de IA personalizado usando o Agent Builder da Elastic para conectar-se ao modelo GPT-OSS da OpenAI em execução localmente. Ao implantar o Elastic e o LLM em sua máquina local, essa arquitetura permite que você aproveite os recursos de IA generativa, mantendo o controle total sobre seus dados, tudo isso sem enviar informações para serviços externos.</p><p>Utilizamos o GPT-OSS 20B como experimento, mas os modelos oficialmente recomendados para o Elastic Agent Builder podem ser consultados <a href="https://www.elastic.co/docs/solutions/search/agent-builder/models#recommended-models">aqui</a>. Se você precisar de recursos de raciocínio mais avançados, existe também a <a href="https://huggingface.co/openai/gpt-oss-120b">variante com 120 parâmetros</a> , que apresenta melhor desempenho em cenários complexos, embora exija uma máquina com especificações mais altas para ser executada localmente. Para obter mais detalhes, consulte a <a href="https://openai.com/open-models/">documentação oficial da OpenAI</a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Principais projetos e aprendizados do Elastic Agent Builder do Cal Hacks 12.0]]></title>
            <description><![CDATA[Explore os principais projetos do Elastic Agent Builder do Cal Hacks 12.0 e mergulhe em nossos insights técnicos sobre arquiteturas Serverless, ES|QL e agentes.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<a href="https://www.youtube.com/watch/GUI0WXNZduo">Watch video on YouTube</a><p>Há algumas semanas, tivemos a incrível oportunidade de patrocinar <a href="https://cal-hacks-12-0.devpost.com/">o Cal Hacks 12.0</a>, um dos maiores hackathons presenciais, com mais de 2.000 participantes vindos de todo o mundo. Oferecemos uma categoria de prêmios dedicada ao melhor uso do Elastic Agent Builder em Serverless, e a resposta foi fenomenal. Em apenas 36 horas, recebemos 29 projetos que utilizaram o Agent Builder de maneiras criativas, desde a criação de ferramentas de inteligência contra incêndios florestais até validadores do StackOverflow.</p><p>Além dos projetos impressionantes, a experiência no Cal Hacks 12.0 também nos proporcionou algo igualmente valioso: feedback rápido e direto de desenvolvedores que estavam tendo contato com nossa Stack pela primeira vez. Hackathons são testes de pressão únicos, com prazos apertados, zero familiaridade prévia e obstáculos imprevisíveis (como as infames quedas de Wi-Fi). Eles revelam exatamente onde a experiência do desenvolvedor se destaca e onde ainda precisa ser aprimorada. Isso é ainda mais importante agora, à medida que os desenvolvedores interagem com o Elastic Stack de novas maneiras, cada vez mais por meio de fluxos de trabalho orientados por LLM. Neste post do blog, vamos explorar mais a fundo o que os participantes criaram com o Agent Builder e o que aprendemos durante o processo.</p><h2>Os projetos vencedores</h2><h3>Primeiro lugar: AgentOverflow</h3><p>Stack Overflow reconstruído para a era do LLM e dos agentes.</p><p>Leia mais sobre AgentOverflow <a href="https://devpost.com/software/agentoverflow">aqui</a>.</p><p>O AgentOverflow resolve um problema que a maioria dos desenvolvedores de IA enfrenta: os LLMs (Learning Learning Machines) têm alucinações, o histórico de bate-papo desaparece e os desenvolvedores perdem tempo resolvendo os mesmos problemas repetidamente.</p><p>O AgentOverflow captura, valida e reapresenta pares reais de problema-solução, para que os desenvolvedores possam quebrar o ciclo de ilusão e lançar produtos mais rapidamente.</p><h4>Como funciona:</h4><p><strong>1. Compartilhar JSON - o &quot;Esquema da Solução&quot;.</strong></p><p>Um clique em um compartilhamento do Claude irá coletar, extrair e montar um JSON de Solução de Compartilhamento, que é um formato estruturado contendo:</p><ul><li>Problema</li><li>Contexto</li><li>Código</li><li>Tags</li><li>Etapas da solução verificadas.</li></ul><p>Um validador (LAVA) verifica e impõe a estrutura; o usuário adiciona uma linha de contexto extra, que então é armazenada e indexada no Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" style="width: 100%;" /><p><strong>2. Encontre a solução</strong></p><p>Quando você ficar preso, clique em <code>Find Solution</code> e o AgentOverflow irá extrair informações da sua conversa atual, usá-las para construir uma consulta e executar uma pesquisa híbrida no Elasticsearch para exibir os resultados:</p><ul><li>Correções classificadas e validadas pela comunidade</li><li>As mesmas instruções que originalmente resolveram o problema.</li></ul><p>Isso permite que os desenvolvedores copiem, colem e desbloqueiem sua sessão atual rapidamente.</p><p><strong>3. MCP - Injeção de contexto para LLMs</strong></p><p>Ao conectar-se às soluções estruturadas armazenadas no Elasticsearch por meio do MCP (Model Context Protocol), os LLMs recebem um contexto de alta qualidade (código, logs, configurações, correções anteriores) em tempo de execução, sem ruído adicional.</p><p>O AgentOverflow utiliza o Agent Builder com o Elasticsearch como uma camada de memória estruturada que injeta contexto relevante nos LLMs. Isso os transforma de chatbots passivos em solucionadores de problemas sensíveis ao contexto.</p><h3>Segundo lugar: MarketMind</h3><p>Uma visão interpretável e em tempo real da energia de mercado, alimentada por seis Agentes Elásticos.</p><p>Leia mais sobre a MarketMind <a href="https://devpost.com/software/marketmind-b6cy2q">aqui</a>.</p><p>A MarketMind conquistou seu espaço ao oferecer aos traders iniciantes uma plataforma que converte dados de mercado fragmentados em sinais claros e em tempo real. Em vez de lidar com a ação do preço, os fundamentos, o sentimento e a volatilidade em diferentes ferramentas, o MarketMind consolida todas essas informações em uma única plataforma, ajudando os traders a obter insights acionáveis. Este projeto também utilizou algumas consultas ES|QL complexas na construção de seus agentes.</p><h4>Como funciona:</h4><p><strong>1. Coletar dados de mercado em tempo real</strong></p><p>O MarketMind extrai métricas de ação de preço, fundamentos, sentimento, volatilidade e risco do Yahoo Finance. Esses dados são ingeridos e organizados em múltiplos índices do Elasticsearch.</p><p><strong>2. Seis agentes especializados analisam o mercado.</strong></p><p>Cada agente, criado com o Agent Builder, concentra-se em uma camada diferente do mercado. Eles leem dados de um índice do Elasticsearch, calculam suas próprias métricas específicas do domínio e geram uma saída JSON padronizada com pontuações e justificativas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" style="width: 100%;" /><p><strong>3. Agregar sinais em um modelo unificado de “energia de mercado”</strong></p><p>Os resultados combinados aparecem como pulsos brilhantes ao redor de cada ação, ilustrando se o ímpeto está aumentando, o risco está crescendo ou o sentimento está mudando.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" style="width: 100%;" /><p><strong>4. Visualize insights</strong></p><p>A interface foi desenvolvida com React e <a href="https://github.com/vercel/next.js">Next.js</a>, utilizando TypeScript, recursos visuais baseados em física SVG e <a href="https://github.com/chartjs">Chart.js</a> para gráficos de velas em tempo real. Isso transforma análises brutas em feedback acionável em tempo real.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" style="width: 100%;" /><h2>Outros projetos interessantes:</h2><p>Aqui estão alguns outros fortes concorrentes que usaram o Elastic em diferentes partes de sua infraestrutura:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" style="width: 100%;" /><p>Encontre <a href="https://cal-hacks-12-0.devpost.com/submissions/search?utf8=%E2%9C%93&amp;prize_filter%5Bprizes%5D%5B%5D=91882">aqui</a> a lista completa dos projetos submetidos à nossa trilha.</p><h2>O que aprendemos com os desenvolvedores</h2><ul><li><strong>O Construtor de Agentes é fácil de usar:</strong></li></ul><p>A maioria das equipes nunca havia usado o Elastic antes e, mesmo assim, conseguiu criar agentes rapidamente com pouco suporte. Realizamos um workshop para aqueles que precisavam de mais orientação, mas a maioria conseguiu importar seus dados e construir um agente para executar ações com base nesses dados.</p><ul><li><strong>Os LLMs se destacam em </strong>consultas<strong><code>kNN</code>, mas ainda precisam de orientação na geração de ES|QL:</strong></li></ul><p>Ao solicitar que o ChatGPT-5 gerasse consultas ES|QL, foram retornadas informações incorretas, frequentemente misturando ES|QL e SQL. Fornecer os documentos ao LLM em um arquivo Markdown pareceu ser uma solução viável.</p><ul><li><strong>Funções ES|QL exclusivas de snapshots vazaram para a documentação:</strong></li></ul><p>As próximas funções de agregação <code>FIRST</code> e <code>LAST</code> foram acidentalmente incluídas em nossa documentação ES|QL. Como fornecemos esses documentos ao ChatGPT, o modelo usou essas funções corretamente, mesmo que elas ainda não estejam disponíveis no Serverless. Graças ao feedback do grupo, a equipe de engenharia rapidamente abriu e incorporou uma correção para remover as funções da documentação publicada (<a href="https://github.com/elastic/elasticsearch/pull/137341">PR #137341</a>).</p><ul><li><strong>Ausência de orientações específicas para Serverless:</strong></li></ul><p>Uma equipe tentou habilitar <code>LOOKUP JOIN</code> em um índice que não foi criado no modo de pesquisa. A mensagem de erro os levou a seguir comandos que não existem no Serverless. Repassamos isso para a equipe de produto, que imediatamente abriu uma solicitação de correção para uma mensagem acionável específica para Serverless. A longo prazo, a visão é ocultar completamente a complexidade da reindexação (<a href="https://github.com/elastic/elasticsearch-serverless/issues/4838">Problema nº 4838</a>).</p><ul><li><strong>Valor dos eventos presenciais:</strong></li></ul><p>Hackathons online são ótimos, mas nada se compara ao feedback rápido que você obtém ao depurar código lado a lado com os desenvolvedores. Acompanhamos as equipes integrando o Agent Builder em diferentes casos de uso, identificamos pontos em que a experiência do desenvolvedor com ES|QL poderia ser aprimorada e corrigimos problemas muito mais rapidamente do que se tivéssemos tentado fazê-lo por meio de canais assíncronos.</p><h2>Conclusão</h2><p>O Cal Hacks 12.0 nos proporcionou mais do que um fim de semana repleto de demonstrações incríveis; também nos deu uma visão de como os novos desenvolvedores estão interagindo com o Elastic Stack. Em apenas 36 horas, vimos equipes começarem a usar o Agent Builder, ingerir dados no Elasticsearch, projetar sistemas multiagentes e testar nossos recursos de diversas maneiras. O evento também nos lembrou por que os eventos presenciais são importantes. Os ciclos de feedback rápidos, as conversas reais e a depuração prática nos ajudaram a entender as necessidades atuais dos desenvolvedores. Estamos entusiasmados em trazer de volta para a equipe de engenharia o que aprendemos. Nos vemos no próximo hackathon.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando uma sala de imprensa para agentes LLM com protocolo A2A e MCP no Elasticsearch: Parte II]]></title>
            <description><![CDATA[Descubra como construir uma redação especializada para agentes LLM em um ambiente híbrido, utilizando o protocolo A2A para colaboração entre agentes e o MCP para acesso a ferramentas no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
            <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>A2A e MCP: o código em ação</h2><p>Este artigo é um complemento ao artigo &quot;Criando uma sala de imprensa com o agente LLM usando os protocolos A2A e MCP no Elasticsearch!&quot;, que explicou os benefícios de implementar as arquiteturas A2A e MCP no mesmo agente para aproveitar ao máximo as vantagens exclusivas de ambas as estruturas. Um <a href="https://github.com/justincastilla/elastic-newsroom">repositório</a> está disponível caso você queira executar a demonstração por conta própria.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" style="width: 100%;" /><p>Vamos analisar como nossos agentes de redação colaboram usando tanto o A2A quanto o MCP para produzir um artigo jornalístico. O repositório que acompanha o projeto, onde é possível ver os agentes em ação, pode ser encontrado <a href="https://github.com/justincastilla/elastic-newsroom">aqui</a>.</p><h3>Etapa 1: Atribuição da história</h3><p>O <strong>chefe de jornalismo</strong> (atuando como cliente) designa uma pauta:</p><pre><code>{
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "assignment": {
      "topic": "Renewable Energy Adoption in Europe",
      "angle": "Policy changes driving solar and wind expansion",
      "target_length": 1200,
      "deadline": "2025-09-30T18:00:00Z"
    }
  }
}</code></pre><h3>Etapa 2: O repórter solicita pesquisa.</h3><p>O <strong>Agente Repórter</strong> reconhece que precisa de informações básicas e delega essa tarefa ao <strong>Agente Pesquisador</strong> por meio do método A2A:</p><pre><code>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "fact_gathering",
    "parameters": {
      "queries": [
        "EU renewable energy capacity 2024",
        "Solar installations growth Europe",
        "Wind energy policy changes 2024"
      ],
      "depth": "comprehensive"
    }
  }
}</code></pre><h3>Etapa 3: O repórter solicita contexto histórico ao Agente de Arquivo.</h3><p>O <strong>agente repórter</strong> reconhece que o contexto histórico fortaleceria a matéria. Ele delega ao <strong>Agente de Arquivo</strong> (com <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">tecnologia A2A do Elastic</a>) via A2A a busca no arquivo de artigos da sala de notícias, que utiliza o Elasticsearch:</p><pre><code>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "archive_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "search_archive",
    "parameters": {
      "query": "European renewable energy policy changes and adoption trends over past 5 years",
      "focus_areas": ["solar", "wind", "policy", "Germany", "France"],
      "time_range": "2019-2024",
      "result_count": 10
    }
  }
}</code></pre><h3>Etapa 4: O Agente de Arquivamento usa o Agente A2A Elástico com MCP</h3><p>O <strong>Agente de Arquivamento</strong> utiliza o Agente A2A da Elastic, que por sua vez usa o MCP para acessar as ferramentas do Elasticsearch. Isso demonstra a arquitetura híbrida onde o A2A permite a colaboração entre agentes enquanto o MCP fornece acesso às ferramentas:</p><pre><code># Archive Agent using Elastic A2A Agent
async def search_historical_articles(self, query_params):
    # The Archive Agent sends a request to Elastic's A2A Agent
    elastic_response = await self.a2a_client.send_request(
        agent="elastic_agent",
        capability="search_and_analyze",
        parameters={
            "natural_language_query": query_params["query"],
            "index_pattern": "newsroom-articles-*",
            "filters": {
                "topics": query_params["focus_areas"],
                "date_range": query_params["time_range"]
            },
            "analysis_type": "trend_analysis"
        }
    )
    
    # Elastic's A2A Agent internally uses MCP tools:
    # - platform.core.search (to find relevant articles)
    # - platform.core.generate_esql (to analyze trends)
    # - platform.core.index_explorer (to identify relevant indices)
    
    return elastic_response</code></pre><p>O <strong>Agente de Arquivamento</strong> recebe dados históricos abrangentes do Agente A2A da Elastic e os retorna ao Reporter:</p><pre><code>{
  "message_type": "task_response",
  "sender": "archive_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "status": "completed",
    "archive_data": {
      "historical_articles": [
        {
          "title": "Germany's Energiewende: Five Years of Solar Growth",
          "published": "2022-06-15",
          "key_points": [
            "Germany added 7 GW annually 2020-2022",
            "Policy subsidies drove 60% of growth"
          ],
          "relevance_score": 0.94
        },
        {
          "title": "France Balances Nuclear and Renewables",
          "published": "2023-03-20",
          "key_points": [
            "France increased renewable target to 40% by 2030",
            "Solar capacity doubled 2021-2023"
          ],
          "relevance_score": 0.89
        }
      ],
      "trend_analysis": {
        "coverage_frequency": "EU renewable stories increased 150% since 2019",
        "emerging_themes": ["policy incentives", "grid modernization", "battery storage"],
        "coverage_gaps": ["Small member states", "offshore wind permitting"]
      },
      "total_articles_found": 47,
      "search_confidence": 0.91
    }
  }
}</code></pre><p>Esta etapa demonstra como o agente A2A da Elastic se integra ao fluxo de trabalho da redação. O Agente de Arquivo (um agente específico para redações) trabalha em conjunto com o Agente A2A da Elastic (um especialista terceirizado) para aproveitar os poderosos recursos de busca e análise do Elasticsearch. O agente da Elastic usa o MCP internamente para acessar as ferramentas do Elasticsearch, demonstrando a clara separação entre a coordenação do agente (A2A) e o acesso às ferramentas (MCP).</p><h3>Etapa 5: O pesquisador utiliza servidores MCP</h3><p>O <strong>Agente Pesquisador</strong> acessa vários servidores MCP para coletar informações:</p><pre><code># Researcher Agent using MCP to access tools
async def gather_facts(self, queries):
    results = []
    
    # Use News API MCP Server
    news_data = await self.mcp_client.invoke_tool(
        server="news_api_mcp",
        tool="search_articles",
        parameters={
            "query": queries[0],
            "date_range": "2024-01-01 to 2024-09-30",
            "sources": ["reuters", "bloomberg", "euractiv"]
        }
    )
    
    # Use Statistics MCP Server
    stats_data = await self.mcp_client.invoke_tool(
        server="eurostat_mcp",
        tool="get_energy_statistics",
        parameters={
            "metrics": ["solar_capacity_mw", "wind_capacity_mw"],
            "countries": ["EU27"],
            "year": 2024
        }
    )
    
    # Use Fact-Checking MCP Server
    verified_facts = await self.mcp_client.invoke_tool(
        server="factcheck_mcp",
        tool="verify_claims",
        parameters={
            "claims": [
                "EU solar capacity increased 40% in 2024",
                "Germany leads in wind installations"
            ]
        }
    )
    
    return self._synthesize_research(news_data, stats_data, verified_facts)</code></pre><h3>Etapa 6: O pesquisador devolve os dados ao repórter.</h3><p>O <strong>Agente de Pesquisa</strong> envia uma pesquisa completa de volta via A2A:</p><pre><code>{
  "message_type": "task_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "status": "completed",
    "research_data": {
      "key_facts": [
        {
          "claim": "EU solar capacity reached 260 GW in 2024",
          "source": "Eurostat Q3 2024 Report",
          "confidence": 0.95,
          "verified": true
        },
        {
          "claim": "Germany installed 12 GW of wind capacity in 2024",
          "source": "German Federal Network Agency",
          "confidence": 0.92,
          "verified": true
        }
      ],
      "statistics": {
        "solar_growth_rate": "35%",
        "wind_growth_rate": "28%"
      },
      "sources_count": 15
    }
  }
}</code></pre><h3>Etapa 7: O repórter escreve o artigo</h3><p>O <strong>Repórter Agente</strong> utiliza os dados da pesquisa e suas próprias capacidades de mestrado em Direito (LLM) para redigir o artigo. Durante a escrita, o Repórter utiliza os servidores MCP para estilo e modelos:</p><pre><code># Reporter Agent writing with MCP assistance
async def write_article(self, research_data, assignment):
    # Get style guidelines via MCP
    style_guide = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="style://editorial/ap_style_guide"
    )
    
    # Get article template via MCP
    template = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="template://articles/news_story"
    )
    
    # Generate article using LLM + research + style
    draft = await self.llm.generate(
        prompt=f"""
        Write a news article following these guidelines:
        {style_guide}
        
        Using this template:
        {template}
        
        Based on this research:
        {research_data}
        
        Assignment: {assignment}
        """
    )
    
    # Self-evaluate confidence in claims
    confidence_check = await self._evaluate_confidence(draft)
    
    return draft, confidence_check</code></pre><h3>Etapa 8: baixa confiança desencadeia nova pesquisa</h3><p>O <strong>agente repórter</strong> avalia sua versão preliminar e constata que uma das afirmações apresenta baixo nível de confiança. Envia outra solicitação ao <strong>Agente Pesquisador</strong>:</p><pre><code>{
  "message_type": "collaboration_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "request_type": "fact_verification",
    "claims": [
      {
        "text": "France's nuclear phase-down contributed to 15% increase in renewable capacity",
        "context": "Discussing policy drivers for renewable growth",
        "current_confidence": 0.45,
        "required_confidence": 0.80
      }
    ],
    "urgency": "high"
  }
}</code></pre><p>O <strong>pesquisador</strong> verifica a alegação usando servidores de checagem de fatos do MCP e retorna informações atualizadas:</p><pre><code>{
  "message_type": "collaboration_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "verified_claims": [
      {
        "original_claim": "France's nuclear phase-down contributed to 15% increase...",
        "verified_claim": "France's renewable capacity increased 18% in 2024, partially offsetting reduced nuclear output",
        "confidence": 0.88,
        "corrections": "Percentage was 18%, not 15%; nuclear phase-down is gradual, not primary driver",
        "sources": ["RTE France", "French Energy Ministry Report 2024"]
      }
    ]
  }
}</code></pre><h3>Etapa 9: O repórter revisa e envia ao editor.</h3><p>O <strong>repórter</strong> incorpora os fatos verificados e envia a versão finalizada ao <strong>editor</strong> por meio do sistema A2A:</p><pre><code>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "editor_agent",
  "payload": {
    "task_id": "edit_renewable_story",
    "parent_task_id": "story_renewable_energy_2024",
    "content": {
      "headline": "Europe's Renewable Revolution: Solar and Wind Surge 30% in 2024",
      "body": "[Full article text...]",
      "word_count": 1185,
      "sources": [/* array of sources */]
    },
    "editing_requirements": {
      "check_style": true,
      "check_facts": true,
      "check_seo": true
    }
  }
}</code></pre><h3>Etapa 10: Revisão do editor usando as ferramentas MCP</h3><p>O <strong>Agente de Edição</strong> utiliza vários servidores MCP para revisar o artigo:</p><pre><code># Editor Agent using MCP for quality checks
async def review_article(self, content):
    # Grammar and style check
    grammar_issues = await self.mcp_client.invoke_tool(
        server="grammarly_mcp",
        tool="check_document",
        parameters={"text": content["body"]}
    )
    
    # SEO optimization check
    seo_analysis = await self.mcp_client.invoke_tool(
        server="seo_mcp",
        tool="analyze_content",
        parameters={
            "headline": content["headline"],
            "body": content["body"],
            "target_keywords": ["renewable energy", "Europe", "solar", "wind"]
        }
    )
    
    # Plagiarism check
    originality = await self.mcp_client.invoke_tool(
        server="plagiarism_mcp",
        tool="check_originality",
        parameters={"text": content["body"]}
    )
    
    # Generate editorial feedback
    feedback = await self._generate_feedback(
        grammar_issues, 
        seo_analysis, 
        originality
    )
    
    return feedback</code></pre><p>O <strong>editor</strong> aprova o artigo e o encaminha:</p><pre><code>{
  "message_type": "task_response",
  "sender": "editor_agent",
  "receiver": "reporter_agent",
  "payload": {
    "status": "approved",
    "quality_score": 9.2,
    "minor_edits": [
      "Changed 'surge' to 'increased' in paragraph 3 for AP style consistency",
      "Added Oxford comma in list of countries"
    ],
    "approved_content": "[Final edited article]"
  }
}</code></pre><h3>Etapa 11: A editora publica via CI/CD</h3><p>Por fim, o <strong>Agente de Impressão</strong> publica o artigo aprovado usando os servidores MCP para o pipeline CMS e CI/CD:</p><pre><code># Publisher Agent publishing via MCP
async def publish_article(self, content, metadata):
    # Upload to CMS via MCP
    cms_result = await self.mcp_client.invoke_tool(
        server="wordpress_mcp",
        tool="create_post",
        parameters={
            "title": content["headline"],
            "body": content["body"],
            "status": "draft",
            "categories": metadata["categories"],
            "tags": metadata["tags"],
            "featured_image_url": metadata["image_url"]
        }
    )
    
    post_id = cms_result["post_id"]
    
    # Trigger CI/CD deployment via MCP
    deploy_result = await self.mcp_client.invoke_tool(
        server="cicd_mcp",
        tool="trigger_deployment",
        parameters={
            "pipeline": "publish_article",
            "environment": "production",
            "post_id": post_id,
            "schedule": "immediate"
        }
    )
    
    # Track analytics
    await self.mcp_client.invoke_tool(
        server="analytics_mcp",
        tool="register_publication",
        parameters={
            "post_id": post_id,
            "publish_time": datetime.now().isoformat(),
            "story_id": metadata["story_id"]
        }
    )
    
    return {
        "status": "published",
        "post_id": post_id,
        "url": f"https://newsroom.example.com/articles/{post_id}",
        "deployment_id": deploy_result["deployment_id"]
    }</code></pre><p>A <strong>editora</strong> confirma a publicação via A2A:</p><pre><code>{
  "message_type": "task_complete",
  "sender": "printer_agent",
  "receiver": "news_chief",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "status": "published",
    "publication": {
      "url": "https://newsroom.example.com/articles/renewable-europe-2024",
      "published_at": "2025-09-30T17:45:00Z",
      "post_id": "12345"
    },
    "workflow_metrics": {
      "total_time_minutes": 45,
      "agents_involved": ["reporter", "researcher", "archive", "editor", "printer"],
      "iterations": 2,
      "mcp_calls": 12
    }
  }
}</code></pre><p>Segue abaixo a sequência completa do fluxo de trabalho A2A no repositório anexo, utilizando os mesmos agentes descritos acima.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">#</td><td style="border: 1px solid #ccc; padding: 4px;">De</td><td style="border: 1px solid #ccc; padding: 4px;">Para</td><td style="border: 1px solid #ccc; padding: 4px;">Ação</td><td style="border: 1px solid #ccc; padding: 4px;">Protocolo</td><td style="border: 1px solid #ccc; padding: 4px;">Descrição</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">Usuário</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Atribuir história</td><td style="border: 1px solid #ccc; padding: 4px;">HTTP POST</td><td style="border: 1px solid #ccc; padding: 4px;">O usuário envia o tema e o enfoque da matéria.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">2</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Criar história</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Cria um registro de história com um ID exclusivo.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Atribuição de Delegado</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Envia a atribuição da matéria através do protocolo A2A</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">4</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Aceitar tarefa</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Atribuição de estoques internamente</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Gerar esboço</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Cria o esboço do artigo e as perguntas de pesquisa.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">6a</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Pesquisador</td><td style="border: 1px solid #ccc; padding: 4px;">Solicitar pesquisa</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Envia perguntas (paralelo com 6b)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">6b</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Arquivista</td><td style="border: 1px solid #ccc; padding: 4px;">Pesquisar no arquivo</td><td style="border: 1px solid #ccc; padding: 4px;">A2A JSONRPC</td><td style="border: 1px solid #ccc; padding: 4px;">Pesquisa artigos históricos (paralelo com 6a)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">7</td><td style="border: 1px solid #ccc; padding: 4px;">Pesquisador</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Questões de pesquisa</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Utiliza a abordagem antropogênica via MCP para responder a perguntas.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">Pesquisador</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Retornar à pesquisa</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Devolve respostas de pesquisa</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">Arquivista</td><td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">Índice de pesquisa</td><td style="border: 1px solid #ccc; padding: 4px;">API REST do ES</td><td style="border: 1px solid #ccc; padding: 4px;">Consultas ao índice news_archive</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10</td><td style="border: 1px solid #ccc; padding: 4px;">Arquivista</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Retornar ao arquivo</td><td style="border: 1px solid #ccc; padding: 4px;">A2A JSONRPC</td><td style="border: 1px solid #ccc; padding: 4px;">Retorna resultados de pesquisa históricos</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">11</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Gerar artigo</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Cria artigo com contexto de pesquisa/arquivo</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">12</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Rascunho da loja</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Salva o rascunho internamente</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">13</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Enviar rascunho</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Entrega a versão finalizada</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">14</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Atualização da história</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Armazena o rascunho e atualiza o status para "rascunho_enviado".</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">15</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Revisão do rascunho</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Encaminha automaticamente para o Editor para revisão.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">16</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Artigo de revisão</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Analisa conteúdo usando Anthropic via MCP.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">17</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Revisão de retorno</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Envia comentários e sugestões editoriais.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">18</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Avaliação da loja</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Feedback do editor de lojas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">19</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Aplicar edições</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Feedback da revisão de rotas para o repórter</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">20</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Aplicar edições</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Revisa o artigo com base no feedback.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">21</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Rascunho atualizado</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Atualiza a versão preliminar com revisões.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">Repórter</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Devolução revisada</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Devolve artigo revisado</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">23</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Atualização da história</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Lojas revisaram a versão preliminar, status para "revisado"</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">24</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Publicar artigo</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Rotas automáticas para o editor</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">25</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Servidor MCP</td><td style="border: 1px solid #ccc; padding: 4px;">Gerar etiquetas</td><td style="border: 1px solid #ccc; padding: 4px;">MCP/HTTP</td><td style="border: 1px solid #ccc; padding: 4px;">Cria etiquetas e categorias</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">26</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Elasticsearch</td><td style="border: 1px solid #ccc; padding: 4px;">Artigo de índice</td><td style="border: 1px solid #ccc; padding: 4px;">API REST do ES</td><td style="border: 1px solid #ccc; padding: 4px;">Indexa o artigo ao índice news_archive</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">27</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Sistema de arquivos</td><td style="border: 1px solid #ccc; padding: 4px;">Salvar Markdown</td><td style="border: 1px solid #ccc; padding: 4px;">Entrada/Saída de Arquivos</td><td style="border: 1px solid #ccc; padding: 4px;">Salva o artigo como .md arquivo em /artigos</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">28</td><td style="border: 1px solid #ccc; padding: 4px;">Editor</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Confirmar publicação</td><td style="border: 1px solid #ccc; padding: 4px;">A2A</td><td style="border: 1px solid #ccc; padding: 4px;">Retorna o status de sucesso</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">29</td><td style="border: 1px solid #ccc; padding: 4px;">Chefe de Notícias</td><td style="border: 1px solid #ccc; padding: 4px;">Interno</td><td style="border: 1px solid #ccc; padding: 4px;">Atualização da história</td><td style="border: 1px solid #ccc; padding: 4px;">-</td><td style="border: 1px solid #ccc; padding: 4px;">Atualiza o status da matéria para "publicada".</td>
                    </tr>
              </tbody>
            </table>
          <h2>Conclusão</h2><p>Tanto o A2A quanto o MCP desempenham papéis importantes no paradigma moderno de infraestrutura de LLM aumentada. A tecnologia A2A oferece flexibilidade para sistemas multiagentes complexos, mas potencialmente menor portabilidade e maior complexidade operacional. O MCP oferece uma abordagem padronizada para integração de ferramentas que é mais simples de implementar e manter, embora não seja projetado para lidar com orquestração multiagente.</p><p>A escolha não é binária. Conforme demonstrado em nosso exemplo de redação, os sistemas mais sofisticados e eficazes baseados em LLM geralmente combinam ambas as abordagens: os agentes se coordenam e se especializam por meio de protocolos A2A, enquanto acessam suas ferramentas e recursos por meio de servidores MCP. Essa arquitetura híbrida proporciona os benefícios organizacionais dos sistemas multiagentes, juntamente com a padronização e as vantagens do ecossistema do MCP. Isso sugere que talvez não seja necessário escolher: basta usar ambos como abordagem padrão.</p><p>Cabe a você, como desenvolvedor ou arquiteto, testar e determinar a melhor combinação de ambas as soluções para obter o resultado adequado ao seu caso de uso específico. Compreender os pontos fortes, as limitações e as aplicações adequadas de cada abordagem permitirá que você construa sistemas de IA mais eficazes, fáceis de manter e escaláveis.</p><p>Seja para criar uma redação digital, uma plataforma de atendimento ao cliente, um assistente de pesquisa ou qualquer outro aplicativo baseado em LLM, considerar cuidadosamente suas necessidades de coordenação (A2A) e requisitos de acesso às ferramentas (MCP) o colocará no caminho do sucesso.</p><h2>Recursos adicionais</h2><ul><li><strong>Construtor de Agentes do Elasticsearch: </strong><a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">https://www.elastic.co/docs/solutions/search/elastic-agent-builder</a></li><li><strong>Especificação A2A</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></li><li><strong>Integração A2A e MCP</strong>: <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">https://a2a-protocol.org/latest/topics/a2a-and-mcp/</a></li><li><strong>Protocolo de Contexto do Modelo</strong>: <a href="https://modelcontextprotocol.io/">https://modelcontextprotocol.io</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Você sabe, para contexto - Parte III: O poder da busca híbrida na engenharia de contexto]]></title>
            <description><![CDATA[Descubra como usar a engenharia de contexto e a busca híbrida para melhorar a precisão dos resultados da IA com agregações, RBAC e sinais não relacionados ao conteúdo.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/context-engineering-hybrid-search-agentic-ai-accuracy</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/context-engineering-hybrid-search-agentic-ai-accuracy</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b82ff25242e4229cc20b218d9cc91c60cfd680bc-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Já discutimos a busca híbrida (<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">Parte I</a>) e a engenharia de contexto (<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">Parte II</a>); agora, vamos explorar como elas funcionam juntas para obter o máximo efeito no fornecimento de contexto direcionado para operações de RAG e IA agente.</p><h2>A busca não morreu, apenas mudou de lugar.</h2><p>Assim, tivemos essa mudança de uma abordagem que consistia principalmente em buscar contexto por meio de uma caixa de texto e usar as informações (o contexto) retornadas para construir as respostas nós mesmos, para agora usar a linguagem natural para dizer a um agente o que queremos e deixar que ele pesquise e compile automaticamente a resposta para nós. Muitos no mundo da tecnologia estão apontando para essa mudança e proclamando que &quot;a busca está morta&quot; (bem, o mundo do SEO e do AdWords está <a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/">definitivamente mudando</a>: alguém aí se lembra <a href="https://www.wired.com/story/goodbye-seo-hello-geo-brandlight-openai/">do GEO</a> ?), mas a busca ainda é absolutamente crucial para as operações de agentes — ela só é realizada, em grande parte, fora do campo de visão, por meio de ferramentas.</p><p>Anteriormente, os humanos eram os principais árbitros da relevância subjetiva: cada usuário tem seus próprios motivos para realizar a busca, e sua experiência pessoal influencia a precisão relativa dos resultados. Para confiarmos que os agentes podem chegar à mesma conclusão (ou melhor) que nós, precisamos garantir que as informações contextuais a que eles têm acesso sejam as mais próximas possíveis da nossa intenção subjetiva. Temos que estruturar o contexto que oferecemos aos mestrados em Direito (LLM) de forma a atingir esse objetivo!</p><h2>Geração de contexto com recuperação de pesquisa híbrida</h2><p>Só para relembrar, lá da Parte I, que a busca híbrida da Elastic combina os pontos fortes da busca tradicional baseada em palavras-chave (flexibilidade de sintaxe, precisão de palavras-chave e pontuação de relevância) com a compreensão semântica da busca por similaridade vetorial e oferece múltiplas técnicas de reclassificação. Essa sinergia (nunca se encontrou um uso mais preciso dessa palavra!) Permite resultados altamente relevantes, com consultas que podem ser muito mais específicas na forma como direcionam o conteúdo. Não se trata apenas de poder aplicar a relevância subjetiva como <em>uma</em> das etapas de recuperação; trata-se, na verdade, de que a recuperação na primeira etapa pode incluir a pontuação de relevância juntamente com todos os outros métodos simultaneamente.</p><h3>Precisão e eficiência superiores</h3><p>Utilizar uma plataforma de dados que possa fornecer busca, recuperação e reclassificação distribuídas como seu principal mecanismo de recuperação de contexto faz muito sentido. Você pode usar uma sintaxe de consulta avançada para adicionar o componente ausente da intenção subjetiva e filtrar o conteúdo que possa distrair ou obscurecer o valor das informações contextuais retornadas. Você pode selecionar qualquer uma das opções de sintaxe individuais disponíveis ou combinar modalidades em uma única pesquisa que visa cada tipo de dado da maneira que melhor o compreende e, em seguida, combiná-los/reordená-los com a reclassificação. Você pode filtrar a resposta para incluir apenas os campos/valores desejados, mantendo os dados irrelevantes afastados. Em termos de suporte aos agentes, essa flexibilidade de segmentação permite criar ferramentas extremamente precisas na forma como recuperam o contexto.</p><h3>Refinamento de contexto (agregações e sinais não relacionados ao conteúdo)</h3><p>As agregações podem ser especialmente úteis para moldar o conteúdo que uma ferramenta fornece à janela de contexto. As agregações fornecem naturalmente informações numéricas sobre o formato dos dados contextuais retornados, o que facilita e torna mais preciso o raciocínio dos Modelos de Aprendizagem Baseados em Leis (LLMs). Como as agregações podem ser hierarquicamente aninhadas, é uma maneira fácil de adicionar detalhes em vários níveis para o LLM, a fim de gerar uma compreensão mais matizada. As agregações também podem ajudar no gerenciamento do tamanho da janela de contexto — você pode facilmente reduzir o resultado de uma consulta de 100 mil documentos para algumas centenas de tokens de insights agregados.</p><p>Os sinais não relacionados ao conteúdo são os indicadores inerentes aos seus dados que fornecem uma visão mais ampla do que você está analisando; são as características adicionais dos resultados, como popularidade, atualidade, localização geográfica, categorias, diversidade de hospedagem ou faixas de preço. Essas informações podem ser úteis para orientar o agente na avaliação da importância do contexto recebido. Alguns exemplos simples podem ajudar a ilustrar isso melhor:</p><ul><li><strong>Impulsionando conteúdo popular e publicado recentemente</strong> - Imagine que você tenha uma base de conhecimento com artigos. Você deseja encontrar artigos relevantes para a consulta de um usuário, mas também quer priorizar artigos que sejam recentes e que tenham sido considerados úteis por outros usuários (por exemplo, que tenham um grande número de &quot;curtidas&quot;). Nesse cenário, podemos usar uma busca híbrida para encontrar artigos relevantes e, em seguida, reclassificá-los com base em uma combinação de sua data de publicação e popularidade.</li><li><strong>Busca em e-commerce com ajuste de vendas e estoque</strong> - Em um ambiente de e-commerce, você deseja mostrar aos clientes produtos que correspondam ao termo de busca, mas também promover produtos que estejam vendendo bem e disponíveis em estoque. Você também pode querer diminuir a classificação de produtos com baixo estoque para evitar a frustração do cliente.</li><li><strong>Priorizando problemas de alta gravidade em um sistema de rastreamento de bugs</strong> - Para uma equipe de desenvolvimento de software, ao procurar problemas, é crucial que os problemas de alta gravidade, alta prioridade e atualizados recentemente sejam exibidos primeiro. Você pode usar indicadores não-sinais, como &quot;criticidade&quot; e &quot;mais discutido&quot;, para ponderar diferentes fatores de forma independente, garantindo que as questões mais críticas e ativamente discutidas cheguem ao topo.</li></ul><p>Essas consultas de exemplo e outras podem ser encontradas na <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/you-know-for-context/">página de conteúdo</a> do Elasticsearch Labs que acompanha este artigo.</p><h3>aplicação das leis de segurança</h3><p>Uma vantagem crucial de utilizar uma camada de velocidade baseada em pesquisa, como o Elastic, para engenharia de contexto é sua estrutura de segurança integrada. A plataforma da Elastic garante que o contexto fornecido às operações de IA generativa e agente respeite e proteja informações confidenciais mantidas em sigilo por meio de controle de acesso baseado em funções (RBAC) e controle de acesso baseado em atributos (ABAC) granulares. Isso significa que não apenas as consultas são processadas com eficiência, mas também que os resultados são filtrados de acordo com as permissões específicas do agente ou do usuário que iniciou a solicitação.</p><p>Os agentes são executados como o usuário autenticado, portanto a segurança é aplicada implicitamente por meio dos recursos de segurança integrados à plataforma:</p><ul><li><strong>Permissões refinadas:</strong> Defina o acesso no nível do documento, do campo ou até mesmo do termo, garantindo que os agentes de IA recebam apenas os dados que estão autorizados a visualizar.</li><li><strong>Controle de acesso baseado em funções (RBAC):</strong> Atribua funções a agentes ou usuários, concedendo acesso a conjuntos de dados ou funcionalidades específicas com base em suas responsabilidades definidas.</li><li><strong>Controle de acesso baseado em atributos (ABAC):</strong> Implemente políticas de acesso dinâmicas com base em atributos dos dados, do usuário ou do ambiente, permitindo uma segurança altamente adaptável e contextualizada.</li><li><strong>Segurança em nível de documento (DLS) e segurança em nível de campo (FLS):</strong> Esses recursos garantem que, mesmo dentro de um documento recuperado, apenas as partes autorizadas sejam visíveis, impedindo que informações confidenciais sejam expostas.</li><li><strong>Integração com segurança corporativa:</strong> Integre-se perfeitamente com sistemas de gerenciamento de identidade existentes (como LDAP, SAML, OIDC) para aplicar políticas de segurança consistentes em toda a organização.</li></ul><p>Ao integrar essas medidas de segurança diretamente no mecanismo de recuperação de contexto, a Elastic atua como um guardião seguro, garantindo que os agentes de IA operem dentro de limites de dados definidos, evitando a exposição não autorizada de dados e mantendo a conformidade com as regulamentações de privacidade de dados. Isso é fundamental para construir confiança em sistemas de IA que lidam com informações confidenciais ou proprietárias.</p><p>Como benefício adicional, ao usar uma camada unificada de velocidade de dados sobre suas fontes de dados corporativas, você alivia as cargas inesperadas de consultas ad hoc nesses repositórios que as ferramentas de agentes criariam. Você obtém um local centralizado para pesquisar tudo em tempo quase real e um único lugar para aplicar controles de segurança e governança.</p><h2>Ferramentas híbridas baseadas em pesquisa</h2><p>Existem algumas funcionalidades essenciais (e <a href="https://www.elastic.co/blog/whats-new-elastic-9-2-0">outras estão sendo adicionadas constantemente</a>) da plataforma Elastic que impulsionam a busca pela engenharia de contexto. O principal aqui é que a plataforma oferece uma infinidade de maneiras de atingir objetivos, com a flexibilidade para adaptar, alterar e expandir os métodos à medida que o ecossistema de IA avança.</p><h3>Apresentando o Construtor de Agentes</h3><p>O Elastic <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder</a> é nossa primeira incursão no mundo das ferramentas de IA com agentes, criadas para interagir com os dados que você já armazena no Elastic. O Agent Builder oferece uma interface de chat que permite aos usuários criar e gerenciar seus próprios agentes e ferramentas dentro do Kibana. Ele vem com servidores MCP e A2A integrados, APIs programáticas e um conjunto de ferramentas de sistema pré-construídas para consultar e explorar índices do Elasticsearch, além de gerar consultas ES|QL a partir de linguagem natural. O Agent Builder permite criar ferramentas personalizadas que visam e moldam os dados contextuais retornados ao agente por meio de uma sintaxe de consulta <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> expressiva.</p><p>Como o ES|QL realiza buscas híbridas, você pergunta? A funcionalidade principal é alcançada através da combinação do tipo de campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> e dos comandos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork">FORK</a>/<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fuse">FUSE</a> (o FUSE usa <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> por padrão para mesclar os resultados de cada fork). Aqui está um exemplo simples de uma busca fictícia de produto:</p><pre><code>FROM products
| FORK
  (MATCH description "high performance gaming laptop" | EVAL search_type = "bm25"),
  (MATCH description_semantic "high performance gaming laptop" | EVAL search_type = "semantic")
| FUSE 
| LIMIT 20
| KEEP product_name, description, _score, search_type</code></pre><p>A cláusula <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL</a> incluída em cada um dos ramos FORK no exemplo acima não é estritamente necessária; ela está incluída apenas para demonstrar como você pode rastrear de qual modalidade de pesquisa um determinado resultado foi retornado.</p><h3>Modelos de pesquisa</h3><p>Digamos que você queira direcionar suas próprias ferramentas externas de gerenciamento de agentes para sua implantação do Elasticsearch. E em vez de ES|QL, você deseja usar recuperadores de vários estágios ou reutilizar a sintaxe DSL existente que você desenvolveu, e também deseja poder controlar as entradas que a consulta aceita, a sintaxe usada para executar a pesquisa e os campos retornados na saída. <a href="https://www.elastic.co/docs/solutions/search/search-templates">Os modelos de pesquisa</a> permitem que os usuários definam estruturas predefinidas para padrões de pesquisa comuns, melhorando a eficiência e a consistência na recuperação de dados. Isso é particularmente benéfico para ferramentas de agentes que interagem com APIs de busca, pois ajuda a padronizar o código repetitivo e permite uma iteração mais rápida na lógica de busca. E se alguma vez precisar ajustar algum desses fatores, basta atualizar o modelo de pesquisa e pronto, as alterações são implementadas. Se você procura um exemplo de modelos de pesquisa em ação com ferramentas agentivas, confira o blog do Elasticsearch Labs &quot; <a href="https://www.elastic.co/search-labs/blog/mcp-intelligent-search">MCP para pesquisa inteligente</a>&quot;, que utiliza um modelo de pesquisa por trás de uma chamada de ferramenta de um servidor MCP externo.</p><h3>Fluxos de trabalho integrados (SIM!)</h3><p>Um dos aspectos mais difíceis de lidar em nosso novo mundo de IA com agentes é a natureza não determinística de agentes &quot;racionais&quot; semiautônomos e autodirigidos. A engenharia de contexto é uma disciplina crítica para a IA ativa: são as técnicas que ajudam a restringir as possíveis conclusões que nosso agente pode gerar ao que sabemos ser verdade fundamental. Mesmo com uma janela de contexto altamente precisa e relevante (quando saímos do âmbito dos fatos numéricos), ainda nos falta aquela garantia de que a resposta do agente seja totalmente repetível e confiável.</p><p>Ao executar a mesma solicitação para um agente várias vezes, as respostas podem ser <em>essencialmente</em> as mesmas, com <em>apenas uma pequena</em> diferença na forma como são enviadas. Isso geralmente funciona bem para consultas simples, talvez seja quase imperceptível, e podemos tentar moldar a saída com técnicas de engenharia de contexto. Mas, à medida que as tarefas que solicitamos aos nossos agentes se tornam mais complexas, aumenta a probabilidade de que uma ou mais subtarefas introduzam uma variação que altere ligeiramente o resultado final. É provável que a situação piore à medida que começarmos a depender mais da comunicação entre agentes, e essas variações se tornarão cumulativas. Isso reforça a ideia de que as ferramentas com as quais nossos agentes interagem precisam ser muito flexíveis e ajustáveis para direcionar com precisão os dados contextuais, e que devem responder em um formato de saída esperado. Isso também indica que, para muitos casos de uso, precisamos direcionar as interações entre agentes e ferramentas — é aí que os fluxos de trabalho entram em cena!</p><p>Em breve, a Elastic terá fluxos de trabalho totalmente personalizáveis integrados ao núcleo da plataforma. Esses fluxos de trabalho poderão operar com agentes e ferramentas de forma bidirecional, ou seja, os fluxos de trabalho poderão chamar agentes e ferramentas, e os agentes e ferramentas poderão chamar fluxos de trabalho. Ter essas funcionalidades totalmente integradas na mesma plataforma de IA de busca onde todos os seus dados residem será transformador; o potencial dos fluxos de trabalho é extremamente empolgante! Em breve, muito em breve!</p><h3>Elástico como banco de memória unificado</h3><p>Por ser uma plataforma de dados distribuída, criada para buscas quase em tempo real, a Elastic executa naturalmente as funções de memória de longo prazo para sistemas de IA com agentes. Com a experiência de chat integrada do Agent Builder, também temos rastreamento e gerenciamento da memória de curto prazo e do histórico de conversas. E como toda a plataforma é orientada a APIs, é extremamente fácil utilizar o Elastic como plataforma para persistir a saída contextual de uma ferramenta (e poder consultá-la posteriormente), o que poderia sobrecarregar a janela de contexto do agente; essa técnica às vezes é chamada de &quot;<a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=Agents%20can%20assemble%20understanding%20layer%20by%20layer%2C%20maintaining%20only%20what%27s%20necessary%20in%20working%20memory%20and%20leveraging%20note%2Dtaking%20strategies%20for%20additional%20persistence">anotações</a> &quot; em círculos de engenharia de contexto.</p><p>Ter memória de curto e longo prazo na mesma plataforma de busca traz muitos benefícios intrínsecos: imagine poder usar históricos de bate-papo e respostas contextuais persistentes como parte dos influenciadores semânticos em interações futuras, ou para realizar análises de ameaças, ou para criar produtos de dados persistentes que são gerados automaticamente a partir de chamadas de ferramentas repetidas com frequência… As possibilidades são infinitas!</p><h2>Conclusão</h2><p>O surgimento de grandes modelos de linguagem mudou a forma como conseguimos relacionar conteúdo e os métodos que usamos para analisar nossos dados. Estamos nos afastando rapidamente do mundo atual, onde os humanos realizam a pesquisa, a análise contextual e o raciocínio lógico para responder às suas próprias perguntas, para um mundo onde essas etapas são amplamente automatizadas por meio de inteligência artificial ativa. Para que possamos confiar nas respostas geradas que recebemos, precisamos ter a garantia de que o agente considerou <em>todas</em> as informações <em>mais relevantes</em> (incluindo o fator de relevância subjetiva) ao gerar sua resposta. Nosso principal método para tornar a IA agente confiável é fundamentar as ferramentas que recuperam contexto adicional por meio de técnicas de RAG (Aleatorização, Atribuição e Geração de Respostas) e engenharia de contexto, mas a forma como essas ferramentas realizam a <em>recuperação inicial</em> pode ser crucial para a precisão da resposta.</p><p>A plataforma Elastic Search AI oferece a flexibilidade e a vantagem da busca híbrida, juntamente com diversos recursos integrados que auxiliam a IA agente em termos de precisão, desempenho e escalabilidade; em outras palavras, o Elastic é uma plataforma fantástica para vários aspectos da engenharia de contexto! Ao padronizar a recuperação de contexto por meio de uma plataforma de busca, simplificamos as operações das ferramentas de inteligência artificial em várias frentes — e, assim como diz o paradoxo &quot;ir mais devagar para ir mais rápido&quot;, a simplicidade na camada de geração de contexto significa uma IA mais rápida e confiável.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sabe, para contexto - Parte II: IA Agêntica e a necessidade de engenharia de contexto]]></title>
            <description><![CDATA[Aprenda como a evolução dos LLMs em direção à IA agente aumenta a necessidade de engenharia de contexto para resolver os limites de contexto RAG e o gerenciamento de memória.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/context-engineering-llm-evolution-agentic-ai</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/context-engineering-llm-evolution-agentic-ai</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6153ce399e5498841ef2ffa5363a3e2d2dea726d-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Com esse <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">contexto</a> (bastante extenso) sobre como os LLMs (Learning Learning Machines) mudaram os processos subjacentes de recuperação de informações, vejamos como eles também mudaram a forma como consultamos dados.</p><h2>Uma nova forma de interagir com dados</h2><p>A IA generativa (genAI) e a IA agentiva funcionam de maneira diferente da busca tradicional. Enquanto antes começávamos a pesquisar informações por meio de uma busca (&quot;deixe-me pesquisar isso no Google...&quot;), a ação inicial tanto para a IA de geração de robôs quanto para os agentes geralmente se dá por meio da linguagem natural inserida em uma interface de bate-papo. A interface de bate-papo é uma discussão com um LLM (Literatura Liderada pelo Senhor da Moeda) que usa sua compreensão semântica para transformar nossa pergunta em uma resposta concisa, uma resposta resumida que parece vir de um oráculo com amplo conhecimento de todos os tipos de informação. O que realmente convence é a capacidade do LLM de gerar frases coerentes e ponderadas que conectam os fragmentos de conhecimento que ele apresenta — mesmo quando são imprecisos ou totalmente alucinatórios, há uma <a href="https://en.wikipedia.org/wiki/Truthiness">sensação de veracidade</a> neles.</p><p>Aquela velha barra de pesquisa com a qual nos acostumamos tanto a interagir pode ser considerada o mecanismo RAG que usávamos quando <strong><em>nós mesmos</em></strong> éramos o agente de raciocínio. Hoje em dia, até mesmo os mecanismos de busca da internet estão transformando nossa tradicional experiência de busca lexical, baseada em &quot;catar e digitar&quot;, em resumos gerados por inteligência artificial que respondem à consulta com um sumário dos resultados, ajudando os usuários a evitar a necessidade de clicar e avaliar cada resultado individualmente.</p><h2>IA Generativa e RAG</h2><p>A IA generativa tenta usar sua compreensão semântica do mundo para analisar a intenção subjetiva expressa em uma solicitação de bate-papo e, em seguida, usa suas habilidades de inferência para criar uma resposta especializada instantaneamente. Uma interação com IA generativa possui várias partes: começa com a entrada/consulta do usuário, conversas anteriores na sessão de bate-papo podem ser usadas como contexto adicional, e a instrução que informa ao LLM como raciocinar e quais procedimentos seguir na construção da resposta. As instruções evoluíram de orientações simples do tipo &quot;explique isso para mim como se eu tivesse cinco anos de idade&quot; para explicações detalhadas de como processar as solicitações. Essas análises geralmente incluem seções distintas que descrevem detalhes da personalidade/função da IA, raciocínio pré-geração/processo de pensamento interno, critérios objetivos, restrições, formato de saída, público-alvo, bem como exemplos para ajudar a demonstrar os resultados esperados.</p><p>Além da consulta do usuário e da mensagem do sistema, a geração aumentada de recuperação (RAG, na sigla em inglês) fornece informações contextuais adicionais no que é chamado de &quot;janela de contexto&quot;. O RAG tem sido uma adição crucial à arquitetura; é o que usamos para informar o LLM sobre as peças que faltam em sua compreensão semântica do mundo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" style="width: 100%;" /><p>As janelas de contexto podem ser um tanto <a href="https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html">exigentes</a> em termos do que, onde e quanto você lhes fornece. O contexto selecionado é muito importante, obviamente, mas a relação sinal-ruído do contexto fornecido também importa, assim como o tamanho da janela.</p><h3>Informação insuficiente</h3><p>Fornecer pouca informação em uma consulta, prompt ou janela de contexto pode levar a alucinações, pois o LLM não consegue determinar com precisão o contexto semântico correto para gerar uma resposta. Existem também problemas com a similaridade vetorial dos tamanhos dos fragmentos de documentos — uma pergunta curta e simples pode não se alinhar semanticamente com os documentos ricos e detalhados encontrados em nossas bases de conhecimento vetorizadas. Foram desenvolvidas técnicas de expansão de consultas, como <a href="https://medium.com/data-science/how-to-use-hyde-for-better-llm-rag-retrieval-a0aa5d0e23e8">Hypothetical Document Embeddings (HyDE)</a> , que utilizam LLMs para gerar uma resposta hipotética mais rica e expressiva do que a consulta curta. O perigo aqui, claro, é que o documento hipotético seja em si uma alucinação que afasta ainda mais o LLM do contexto correto.</p><h3>Informação em excesso</h3><p>Assim como acontece conosco, humanos, o excesso de informações em uma janela de contexto pode sobrecarregar e confundir um usuário de linguagem natural sobre quais são as partes importantes. O estouro de contexto (ou &quot;<a href="https://research.trychroma.com/context-rot">deterioração de contexto</a> &quot;) afeta a qualidade e o desempenho das operações de IA generativa; ele impacta significativamente o &quot;orçamento de atenção&quot; do LLM (sua memória de trabalho) e dilui a relevância entre muitos tokens concorrentes. O conceito de &quot;deterioração do contexto&quot; também inclui a observação de que os autores de livros didáticos tendem a ter um <a href="https://alexandrabarr.beehiiv.com/p/context-windows">viés posicional</a> — eles preferem o conteúdo no início ou no final de uma janela de contexto em relação ao conteúdo na seção intermediária.</p><h3>Informações que distraem ou são contraditórias</h3><p>Quanto maior for a janela de contexto, maior será a probabilidade de incluir informações supérfluas ou conflitantes que podem distrair o usuário do LLM (Liderança em Aprendizagem) de selecionar e processar o contexto correto. De certa forma, isso se torna um problema de &quot;lixo entra, lixo sai&quot;: simplesmente despejar um conjunto de resultados de documentos em uma janela de contexto fornece ao LLM muita informação para processar (potencialmente em excesso), mas dependendo de como o contexto foi selecionado, há uma possibilidade maior de informações conflitantes ou irrelevantes se infiltrarem.</p><h2>IA Agêntica</h2><p>Eu disse que havia muito o que abordar, mas conseguimos — finalmente estamos falando sobre tópicos de IA agente! A IA Agética é uma nova e empolgante aplicação das interfaces de chat do LLM que expande a capacidade da IA generativa (podemos já chamá-la de &quot;legada&quot;?) de sintetizar respostas com base em seu próprio conhecimento e nas informações contextuais fornecidas pelo usuário. À medida que a IA generativa amadureceu, percebemos que havia um certo nível de tarefas e automação que poderíamos delegar aos LLMs, inicialmente relegadas a atividades tediosas e de baixo risco que podem ser facilmente verificadas/validadas por um humano. Em um curto período de tempo, esse escopo inicial cresceu: uma janela de bate-papo do LLM agora pode ser a faísca que envia um agente de IA para planejar, executar e avaliar e adaptar seu plano de forma autônoma e iterativa para atingir o objetivo especificado. Os agentes têm acesso ao raciocínio dos seus LLMs, ao histórico de conversas e à memória cognitiva (na medida do possível), e também dispõem de ferramentas específicas que podem utilizar para atingir esse objetivo. Também estamos vendo agora arquiteturas que permitem que um agente de nível superior funcione como orquestrador de múltiplos <a href="https://www.philschmid.de/the-rise-of-subagents">subagentes</a>, cada um com suas próprias cadeias lógicas, conjuntos de instruções, contexto e ferramentas.</p><p>Os agentes são o ponto de entrada para um fluxo de trabalho em grande parte automatizado: eles são autônomos, pois conseguem conversar com um usuário e, em seguida, usar a &quot;lógica&quot; para determinar quais ferramentas estão disponíveis para ajudar a responder à pergunta do usuário. As ferramentas são geralmente consideradas passivas em comparação com os agentes e são construídas para realizar um único tipo de tarefa. Os <em>tipos</em> de tarefas que uma ferramenta pode executar são praticamente ilimitados (o que é realmente empolgante!), mas uma das principais tarefas que as ferramentas realizam é coletar informações contextuais para que um agente as considere ao executar seu fluxo de trabalho.</p><p>Como tecnologia, a IA ativa ainda está em sua infância e propensa ao equivalente acadêmico do transtorno de déficit de atenção — ela facilmente esquece o que lhe foi pedido para fazer e, muitas vezes, sai fazendo outras coisas que não faziam parte do escopo da tarefa. Por trás da aparente magia, as habilidades de &quot;raciocínio&quot; dos LLMs ainda se baseiam em prever o próximo token mais provável em uma sequência. Para que o raciocínio (ou, um dia, a inteligência artificial geral (IAG)) se torne confiável e digno de confiança, precisamos ser capazes de verificar se, ao recebermos as informações corretas e mais atualizadas, elas raciocinarão da maneira que esperamos (e talvez nos forneçam aquela informação extra que não havíamos imaginado). Para que isso aconteça, as arquiteturas agentivas precisarão da capacidade de se comunicar claramente (protocolos), de aderir aos fluxos de trabalho e restrições que lhes impomos (diretrizes), de lembrar em que ponto da tarefa estão (estado), de gerenciar seu espaço de memória disponível e de validar se suas respostas são precisas e atendem aos critérios da tarefa.</p><h2>Fale comigo em uma língua que eu possa entender.</h2><p>Como é comum em novas áreas de desenvolvimento (especialmente no mundo dos LLMs), inicialmente existiram várias abordagens para a comunicação entre agentes e ferramentas, mas elas rapidamente convergiram para o <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Protocolo de Contexto do Modelo (MCP)</a> como o padrão de facto. A definição de Protocolo de Contexto de Modelo está literalmente no nome: é o <strong>protocolo</strong> que um <strong>modelo</strong> usa para solicitar e receber informações <strong>contextuais</strong> . O MCP funciona como um adaptador universal para que os agentes LLM se conectem a ferramentas e fontes de dados externas; ele simplifica e padroniza as APIs para que diferentes estruturas e ferramentas LLM possam interoperar facilmente. Isso faz do MCP uma espécie de ponto de articulação entre a lógica de orquestração e os comandos do sistema dados a um agente para que ele execute tarefas de forma autônoma a serviço de seus objetivos, e as operações enviadas às ferramentas para que sejam executadas de maneira mais isolada (isolada, pelo menos, em relação ao agente iniciador).</p><p>Este ecossistema é tão novo que cada direção de expansão parece uma nova fronteira. Temos protocolos semelhantes para interações agente-a-agente (<a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">Agent2Agent (A2A)</a> , claro!), bem como outros projetos para melhorar a memória de raciocínio do agente (<a href="https://venturebeat.com/ai/new-memory-framework-builds-ai-agents-that-can-handle-the-real-worlds">ReasoningBank</a>), para selecionar o melhor servidor MCP para a tarefa em questão (<a href="https://arxiv.org/abs/2505.03275">RAG-MCP</a>) e usar análise semântica, como classificação zero-shot e detecção de padrões na entrada e saída, como <a href="https://openai.github.io/openai-guardrails-python/">Guardrails</a> para controlar sobre o que um agente pode operar.</p><p>Você deve ter percebido que a intenção subjacente de cada um desses projetos é melhorar a qualidade e o controle das informações retornadas para uma janela de contexto do agente/genAI? Embora o ecossistema de IA agente continue a desenvolver a capacidade de lidar melhor com essas informações contextuais (para controlá-las, gerenciá-las e operá-las), sempre haverá a necessidade de recuperar as informações contextuais <em>mais relevantes</em> como matéria-prima para o agente processar.</p><h2>Bem-vindo à engenharia de contexto!</h2><p>Se você está familiarizado com os termos de IA generativa, provavelmente já ouviu falar de &#x27;engenharia de prompts&#x27; - a essa altura, é quase uma pseudociência em si mesma. A engenharia de prompts é usada para encontrar as melhores e mais eficientes maneiras de descrever proativamente os comportamentos que você deseja que o LLM utilize ao gerar sua resposta. A &quot; <a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">engenharia de contexto</a>&quot; estende as técnicas de &quot;engenharia de prompts&quot; além do lado do agente, abrangendo também as fontes e sistemas de contexto disponíveis no lado das ferramentas do protocolo MCP, e inclui os tópicos gerais de gerenciamento, processamento e geração de contexto:</p><ul><li><strong>Gerenciamento de contexto </strong>- Relacionado à manutenção da eficiência de estado e contexto em fluxos de trabalho de agentes de longa duração e/ou mais complexos. Planejamento, acompanhamento e orquestração iterativos de tarefas e chamadas de ferramentas para atingir os objetivos do agente. Devido ao limitado &quot;orçamento de atenção&quot; com que os agentes têm que trabalhar, o gerenciamento de contexto se preocupa principalmente com técnicas que ajudam a refinar a janela de contexto para capturar tanto o escopo mais completo quanto os elementos mais importantes do contexto (sua precisão versus abrangência!). As técnicas incluem compressão, sumarização e persistência do contexto de etapas anteriores ou chamadas de ferramentas para liberar espaço na memória de trabalho para contexto adicional em etapas subsequentes.</li><li><strong>Processamento de contexto </strong>- Os passos lógicos e, idealmente, em sua maioria programáticos para integrar, normalizar ou refinar o contexto adquirido de fontes distintas, de modo que o agente possa raciocinar sobre todo o contexto de maneira relativamente uniforme. O objetivo principal é tornar o contexto de todas as fontes (sugestões, RAG, memória, etc.) o mais acessível possível ao agente. </li><li><strong>Geração de contexto </strong>- Se o processamento de contexto visa tornar o contexto recuperado utilizável para o agente, então a geração de contexto permite que o agente solicite e receba informações contextuais adicionais conforme desejar, mas também com restrições.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" style="width: 100%;" /><p>Os diversos elementos efêmeros dos aplicativos de bate-papo do LLM se relacionam diretamente (e às vezes de maneiras sobrepostas) com essas funções de alto nível da engenharia de contexto:</p><ul><li><strong>Instruções / avisos do sistema</strong> - Os avisos servem de base para que a atividade de IA generativa (ou agentiva) direcione seu raciocínio para atingir o objetivo do usuário. Os prompts são um contexto por si só; não são apenas instruções de tom — frequentemente incluem lógica de execução da tarefa e regras para coisas como &quot;pensar passo a passo&quot; ou &quot;respirar fundo&quot; antes de responder, para validar se a resposta atende completamente à solicitação do usuário. Testes recentes demonstraram que as linguagens de marcação são muito eficazes para estruturar as diferentes partes de um enunciado, mas também é preciso ter cuidado para calibrar as instruções, encontrando um equilíbrio ideal entre serem vagas demais e específicas demais; queremos fornecer instruções suficientes para que o LLM encontre o contexto correto, mas não ser tão prescritivos a ponto de perder insights inesperados.</li><li><strong>Memória de curto prazo</strong> (estado/histórico) - A memória de curto prazo consiste essencialmente nas interações da sessão de bate-papo entre o usuário e o LLM. Essas informações são úteis para refinar o contexto em sessões ao vivo e podem ser salvas para consulta e continuação futuras. </li><li><strong>Memória de longo prazo</strong> - A memória de longo prazo deve consistir em informações que sejam úteis em múltiplas sessões. E não se trata apenas de bases de conhecimento específicas de domínio acessadas por meio do RAG; pesquisas recentes utilizam os resultados de solicitações anteriores de IA agentiva/generativa para aprender e referenciar em interações agentivas atuais. Algumas das inovações mais interessantes na área da memória de longo prazo estão relacionadas ao ajuste da forma como o estado é <a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">armazenado e vinculado,</a> para que os agentes possam retomar de onde pararam. </li><li><strong>Saída estruturada</strong> - A cognição exige esforço, então provavelmente não é surpresa que, mesmo com capacidades de raciocínio, os LLMs (assim como os humanos) queiram despender menos esforço ao pensar e, na ausência de uma API ou protocolo definido, ter um mapa (um esquema) de como ler os dados retornados por uma chamada de ferramenta é extremamente útil. A inclusão de <a href="https://platform.openai.com/docs/guides/structured-outputs?lang=javascript">Saídas Estruturadas</a> como parte da estrutura agentiva ajuda a tornar essas interações máquina a máquina mais rápidas e confiáveis, com menos necessidade de análise sintática guiada pelo pensamento.</li><li><strong>Ferramentas disponíveis</strong> - As ferramentas podem realizar todo tipo de tarefa, desde coletar informações adicionais (por exemplo, enviar consultas RAG para repositórios de dados corporativos ou por meio de APIs online) até executar ações automatizadas em nome do agente (como reservar um quarto de hotel com base nos critérios da solicitação do agente). As ferramentas também podem ser subagentes com suas próprias cadeias de processamento. </li><li><strong>Geração Aumentada por Recuperação (RAG)</strong> - Eu realmente gosto da descrição de RAG como &quot;integração dinâmica de conhecimento&quot;. Conforme descrito anteriormente, RAG é a técnica para fornecer as informações adicionais às quais o LLM não teve acesso durante seu treinamento, ou seja, é uma reiteração das ideias que consideramos mais importantes para obter a resposta correta — aquela que é mais relevante para nossa pergunta subjetiva.</li></ul><h2>Poder cósmico fenomenal, espaço vital minúsculo!</h2><p>A IA agente tem muitos novos domínios fascinantes e empolgantes para explorar! Ainda existem muitos dos antigos problemas tradicionais de recuperação e processamento de dados a serem resolvidos, mas também novas classes de desafios que só agora estão vindo à tona na nova era dos LLMs. Muitos dos problemas imediatos que estamos enfrentando hoje estão relacionados à engenharia de contexto, ou seja, a como fornecer aos LLMs (Learning Learning Machines - Máquinas de Memória de Longo Prazo) as informações contextuais adicionais de que precisam sem sobrecarregar seu espaço limitado de memória de trabalho.</p><p>A flexibilidade de agentes semiautônomos que têm acesso a uma variedade de ferramentas (e outros agentes) dá origem a tantas novas ideias para implementar IA que é difícil imaginar as diferentes maneiras pelas quais poderíamos juntar as peças. A maior parte da pesquisa atual se enquadra no campo da engenharia de contexto e está focada na construção de estruturas de gerenciamento de memória que possam lidar e rastrear quantidades maiores de contexto — isso porque os problemas de raciocínio profundo que realmente queremos que os LLMs resolvam apresentam maior complexidade e etapas de pensamento mais longas e multifásicas, onde a memorização é extremamente importante.</p><p>Grande parte da experimentação em curso na área visa encontrar a gestão de tarefas e as configurações de ferramentas ideais para alimentar a &quot;boca&quot; dos agentes. Cada chamada de ferramenta na cadeia de raciocínio de um agente acarreta um custo cumulativo, tanto em termos de computação necessária para executar a função dessa ferramenta quanto em termos do impacto na janela de contexto limitada. Algumas das técnicas mais recentes para gerenciar o contexto de agentes LLM causaram efeitos em cadeia indesejados, como o &quot;<a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai">colapso de contexto</a> &quot;, em que a compressão/resumo do contexto acumulado para tarefas de longa duração resulta em perda <em>excessiva</em> de dados. O objetivo é obter ferramentas que retornem um contexto conciso e preciso, sem que informações irrelevantes ocupem o valioso espaço de memória da janela de contexto.</p><h3>Tantas possibilidades</h3><p>Desejamos separação de funções com flexibilidade para reutilizar ferramentas/componentes, portanto, faz todo o sentido criar ferramentas dedicadas e automatizadas para conectar-se a fontes de dados específicas — cada ferramenta pode se especializar em consultar um tipo de repositório, um tipo de fluxo de dados ou até mesmo um caso de uso. Mas atenção: na ânsia de economizar tempo/dinheiro/provar que algo é possível, haverá uma forte tentação de usar os LLMs como ferramenta de federação… Tente não fazer isso, já passamos <a href="https://www.elastic.co/pdf/elastic-distributed-not-federated-search.pdf">por essa situação</a> antes! A consulta federada funciona como um &quot;tradutor universal&quot; que converte uma consulta recebida na sintaxe que o repositório remoto entende e, em seguida, precisa racionalizar os resultados de múltiplas fontes em uma resposta coerente. A federação como técnica <em>funciona</em> <em>bem</em> em pequenas escalas, mas em grandes escalas, e especialmente quando os dados são multimodais, a federação tenta preencher lacunas que são simplesmente muito grandes.</p><p>No mundo agentivo, o agente seria o federador e as ferramentas (através do MCP) seriam as conexões definidas manualmente com recursos distintos. Utilizar ferramentas específicas para acessar fontes de dados desconectadas pode parecer uma nova e poderosa maneira de unir dinamicamente diferentes fluxos de dados para cada consulta, mas usar essas ferramentas para fazer a mesma pergunta a várias fontes provavelmente acabará causando mais problemas do que soluções. Cada uma dessas fontes de dados provavelmente consiste em diferentes tipos de repositórios subjacentes, cada um com suas próprias capacidades de recuperar, classificar e proteger os dados neles contidos. Essas variações ou &quot;incompatibilidades de impedância&quot; entre os repositórios aumentam a carga de processamento, obviamente. Eles também podem introduzir informações ou sinais conflitantes, onde algo aparentemente inócuo como um desalinhamento na pontuação pode alterar drasticamente a importância atribuída a um trecho do contexto retornado e afetar a relevância da resposta gerada no final.</p><h3>A troca de contexto também é difícil para os computadores.</h3><p>Quando você envia um agente em uma missão, muitas vezes a primeira tarefa dele é encontrar todos os dados relevantes aos quais ele tem acesso. Assim como acontece com os humanos, se cada fonte de dados à qual o agente se conecta responde com informações diferentes e desagregadas, haverá uma carga cognitiva (embora não exatamente do mesmo tipo) associada à extração dos elementos contextuais relevantes do conteúdo recuperado. Isso requer tempo/computação, e cada pequeno detalhe se soma na cadeia lógica agentiva. Isso nos leva à conclusão de que, assim como está sendo discutido para <a href="https://blog.cloudflare.com/code-mode/">o MCP</a>, a maioria das ferramentas de agentes deveria se comportar mais como APIs — funções isoladas com entradas e saídas conhecidas, ajustadas para atender às necessidades de diferentes tipos de agentes. Aliás, estamos até percebendo que <a href="https://arxiv.org/html/2501.12372v5">os LLMs precisam de contexto para contexto</a> — eles se saem muito melhor em conectar os pontos semânticos, especialmente quando se trata de uma tarefa como traduzir linguagem natural para sintaxe estruturada, quando têm um esquema ao qual se referir (leia o manual, de fato!).</p><h2>Intervalo da sétima entrada!</h2><p>Já abordamos o <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">impacto que os LLMs tiveram na recuperação e consulta de dados</a>, bem como a forma como a janela de bate-papo está evoluindo para uma experiência de IA ativa. Vamos juntar os dois tópicos e ver como podemos usar nossos recursos modernos de busca e recuperação para melhorar nossos resultados em engenharia de contexto. Vamos para a <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy">Parte III: O poder da busca híbrida na engenharia de contexto</a>!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como implantar o Elasticsearch no Azure AKS automaticamente]]></title>
            <description><![CDATA[Aprenda como implantar o Elasticsearch com o Kibana no Azure usando o AKS Automatic e o ECK para uma configuração parcialmente gerenciada do Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-azure-aks-automatic-deployment</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-azure-aks-automatic-deployment</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Fri, 14 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4d821659a606e04390b09215e9a0d32eb01f0d1b-854x489.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Este artigo faz parte de uma série onde aprenderemos como instalar o Elasticsearch usando diferentes infraestruturas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a65ca8b62fd8a42d7751b8f4bf057e33d877304-940x458.png" style="width: 100%;" /><p>O ECK exige um esforço significativamente maior do que as soluções Elastic Cloud baseadas no Marketplace, mas é mais automatizado do que implantar VMs por conta própria, porque o operador do Kubernetes cuidará da orquestração do sistema e do escalonamento dos nós.</p><p>Desta vez, vamos trabalhar com o Azure Kubernetes Service (AKS), usando o modo automático. Nos outros artigos, você aprenderá como usar <a href="https://www.elastic.co/search-labs/blog/azure-elasticsearch-vm-deployment">as VMs do Azure</a> e <a href="https://www.elastic.co/search-labs/blog/deploy-elasticsearch-azure-marketplace">o Azure Marketplace</a>.</p><h2>O que é o AKS Automatic?</h2><p><a href="https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic">O Azure Kubernetes Service (AKS) gerencia automaticamente </a>a configuração do cluster, aloca recursos dinamicamente e integra as melhores práticas de segurança, preservando a flexibilidade do Kubernetes, permitindo que os desenvolvedores passem da imagem do contêiner para o aplicativo implantado em minutos.</p><p>O AKS Automatic elimina a maior parte da sobrecarga de gerenciamento de clusters e encontra um bom equilíbrio entre simplicidade e flexibilidade. A escolha certa depende do seu caso de uso, mas a decisão fica mais fácil se você planeja:</p><ul><li><strong>Implante um ambiente de teste: </strong>A implantação é rápida e simples, tornando-a ideal para experimentos rápidos ou clusters de curta duração.</li><li><strong>Trabalhe sem requisitos rígidos de VM, armazenamento ou rede: </strong>o AKS Automatic fornece configurações padrão predefinidas; portanto, se elas atenderem às suas necessidades, você evita configurações adicionais.</li><li><strong>Comece a usar o Kubernetes pela primeira vez: </strong>ao lidar com grande parte da configuração do cluster, o AKS Automatic reduz a curva de aprendizado e permite que as equipes se concentrem em seus aplicativos.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2e4c09b8c5e0ce5e8ea9c369626a373b7030a5ba-854x489.png" style="width: 100%;" /><p>Para o Elasticsearch, usaremos <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">o Elastic Cloud on Kubernetes </a>(ECK), que é o operador oficial do Elastic Kubernetes e simplifica a orquestração das implantações do Elastic Stack no Kubernetes.</p><h2>Como configurar o AKS automaticamente</h2><p>1. Faça login no <a href="https://azure.microsoft.com/">Portal do Microsoft Azure</a>.</p><p>2. No <strong>canto superior direito, </strong>clique<strong> </strong>No botão do<strong> Cloud Shell</strong> , você pode acessar o console e implantar o cluster AKS a partir daí. Alternativamente, você pode usar o <a href="https://learn.microsoft.com/en-us/azure/cloud-shell/overview">Azure Cloud Shell</a>.</p><p><strong><em>Lembre-se de atualizar o ID do projeto com o seu durante o tutorial.</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0aa60605777c0a6e3aef8faa4e54388c2cb582c8-624x495.png" style="width: 100%;" /><p><em>Abrir o AKS deve ser semelhante à captura de tela acima.</em></p><p>3. Instale a extensão aks-preview da CLI do Azure. Esta versão de pré-visualização permitirá selecionar <code>--sku automatic</code> na criação do cluster, o que habilita o recurso AKS Automático.</p><pre><code>az extension add --name aks-preview</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2e05ab67fc554c5fb5208683c179fdeaeadd95db-624x56.png" style="width: 100%;" /><p><em>Se você vir esta mensagem, significa que a extensão AKS foi instalada corretamente.</em></p><p>4. Registre<a href="https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-feature-management"> os sinalizadores de recursos</a> usando o comando <code>az feature register</code></p><pre><code>az feature register --namespace Microsoft.ContainerService --name AutomaticSKUPreview</code></pre><p><em>Você verá os detalhes sobre a assinatura do recurso que acabamos de criar:</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aca710c1f312ba91de461638e518386919ec722-801x138.png" style="width: 100%;" /><p>Verifique o status do registro até que ele mude de “<strong><em>Registrando</em></strong>” para “<strong><em>Registrado</em></strong>”. O processo de cadastro pode levar alguns minutos.</p><pre><code>az feature show --namespace Microsoft.ContainerService --name AutomaticSKUPreview</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/186486b08ab8e1c372efaff50f10cbddeaf4e0cd-844x177.png" style="width: 100%;" /><p>Execute <code>az provider register</code> para propagar as alterações.</p><pre><code>az provider register --namespace Microsoft.ContainerService</code></pre><p>5. Criar um grupo de recursos</p><p>Um grupo de recursos é um grupo lógico de recursos do Azure que serão gerenciados e implantados.</p><pre><code>az group create --name elastic-resource --location eastus</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d1952269e97d94f914020754bd02702f9eafd037-770x212.png" style="width: 100%;" /><p>6. Crie um cluster do Autopilot. Vamos chamá-lo de <strong><em>myAKSAutomaticCluster </em></strong>e usar o grupo de recursos que acabamos de criar. Certifique-se de ter <strong><em>16 vCPUs</em></strong> disponíveis em qualquer um dos seguintes tamanhos de VM: <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dpsv5-series">Standard_D4pds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dldsv5-series">Standard_D4lds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dadsv5-series">Standard_D4ads_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddsv5-series">Standard_D4ds_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddv5-series">Standard_D4d_v5</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/ddv4-series">Standard_D4d_v4</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dsv3-series">Standard_DS3_v2</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/memory-optimized/dv2-dsv2-series-memory">Standard_DS12_v2</a> para que o AKS possa alocar recursos.</p><pre><code>az aks create \
    --resource-group elastic-resource \
    --name myAKSAutomaticCluster \
    --sku automatic \
    --generate-ssh-keys</code></pre><p><em>* Se você receber </em>dois erros<em><code>MissingSubscriptionRegistration</code>), volte ao passo 4 com as assinaturas ausentes. Por exemplo, <code>The subscription is not registered to use namespace &#x27;<strong>microsoft.insights</strong>&#x27;</code> requer a execução de <code>az provider register --namespace Microsoft.Insights.</code></em></p><p>Siga as instruções de login interativo:</p><p><em>Uma mensagem solicitando a execução do comando “az login” será exibida. Você precisa executar esse comando e depois esperar.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/14c0238f755fe6347519e69d3cb28c0fa52ec044-775x203.png" style="width: 100%;" /><p>7. Aguarde até que esteja pronto. Leva cerca de 10 minutos para criar.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb00c3ad18f884f47db6645b196808ebec07c1fc-797x177.png" style="width: 100%;" /><p>8. Configure o acesso à linha de comando do kubectl.</p><pre><code>az aks get-credentials --resource-group elastic-resource --name myAKSAutomaticCluster</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5136d72a5d455345b0b6205bb232c4bdf7762998-793x52.png" style="width: 100%;" /><p><em>Observe que a extensão que instalamos está habilitando o AKS Automático.</em></p><p>9. Confirme se os nós foram implantados.</p><pre><code>kubectl get nodes</code></pre><p>Você verá uma mensagem de erro de acesso proibido; copie o ID do usuário da mensagem de erro.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d6c021fa54f4db00d2d795f5ba9b5a93376d03cd-818x47.png" style="width: 100%;" /><p>10. Adicione seu usuário ao controle de acesso do AKS.</p><p>Obtenha o ID da AKS. Copie a saída do comando.</p><pre><code>az aks show --resource-group elastic-resource  --name myAKSAutomaticCluster --query id --output tsv</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/22a1cdc538bd379812a752c6a368a0651000abb8-810x36.png" style="width: 100%;" /><p>Crie uma atribuição de função usando o ID do AKS e o ID principal do seu usuário.</p><pre><code>az role assignment create --role "Azure Kubernetes Service RBAC Cluster Admin" --assignee <PRINCIPAL_ID> --scope <AKS_ID></code></pre><p>11. Tente confirmar novamente se os nós foram implantados.</p><pre><code>kubectl get nodes</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c9b3a5c1cc540ef732c3e7f60b0a973bdbd0b6fd-617x99.png" style="width: 100%;" /><p>12. Instale o operador Elastic Cloud on the Kubernetes (ECK).</p><pre><code># Install ECK Custom Resource Definitions
kubectl create -f https://download.elastic.co/downloads/eck/2.16.1/crds.yaml

# Install the ECK operator
kubectl apply -f https://download.elastic.co/downloads/eck/2.16.1/operator.yaml</code></pre><p>13. Vamos criar uma instância Elasticsearch de nó único com os valores padrão.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF</code></pre><p>Desativamos <code>nmap</code> porque a máquina AKS padrão tem um valor <code>vm.max_map_count</code> muito baixo. Desativá-lo não é recomendado para produção, mas aumentar o valor de <code>vm.max_map_count</code>. Você pode ler mais sobre como fazer isso <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/virtual-memory">aqui</a>.</p><p>14. Vamos também implantar um cluster Kibana de nó único. Para o Kibana, adicionaremos um balanceador de carga, que nos fornecerá um IP externo que poderemos usar para acessar o Kibana a partir do nosso dispositivo.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
EOF</code></pre><p>Por padrão, o AKS Automatic configurará o balanceador de carga como público; você pode alterar esse comportamento definindo a anotação de metadados:</p><p><code>service.beta.kubernetes.io/azure-load-balancer-internal: &quot;true&quot;</code></p><p>15. Verifique se seus pods estão em execução.</p><pre><code>kubectl get pods</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/213a897176c0af6cea19c7c777cfaf8734e3ee6e-616x84.png" style="width: 100%;" /><p>16. Você também pode executar <code>kubectl get elasticsearch</code> e <code>kubectl get kibana</code> para estatísticas mais específicas, como versão do Elasticsearch, nós e integridade.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c84988e725ef892eddd8fb7e5a03d58c35a8f9d6-470x62.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/452dd03d314cd00c8a3c19e19862b968592a0435-415x62.png" style="width: 100%;" /><p>17. Acesse seus serviços.</p><pre><code>kubectl get svc</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b2d3e8f368be22b89aa2ed4d4d514f97dd6cbabd-624x115.png" style="width: 100%;" /><p>Isso mostrará o <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/accessing-services">URL externo</a> do Kibana em EXTERNAL-IP. Pode levar alguns minutos para o balanceador de carga ser provisionado. <strong><em>Copie o valor de EXTERNAL-IP.</em></strong></p><p>18. Obtenha a senha do Elasticsearch para o usuário &#x27;elastic&#x27;:</p><pre><code>kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bd74537f8c35c4e027c518913fdb0a0524621d56-624x31.png" style="width: 100%;" /><p>19. <strong>Acesse o Kibana</strong> através do seu navegador:</p><p>a. URL: https://:5601&lt;EXTERNAL_IP&gt;</p><p>b. Nome de usuário: elastic</p><p>c. Senha: c44A295CaEt44D6xIzN6Zs5m (da etapa anterior)</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a601112527d80721b292328ed8da58386d2837eb-463x503.png" style="width: 100%;" /><p>20. Ao acessar o Elastic Cloud pelo seu navegador, você verá a tela de boas-vindas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f91e71fa961d215a8d886601d1a9fc5c452ce329-1999x1256.png" style="width: 100%;" /><p>Se você deseja alterar as especificações do cluster Elasticsearch, como alterar ou redimensionar nós, pode aplicar o manifesto YML novamente com as novas configurações:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
    - name: default
      count: 2
      config:
        node.store.allow_mmap: false
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests:
                  memory: 1.5Gi
                  cpu: 2
                limits:
                  memory: 1.5Gi
                  cpu: 2
EOF</code></pre><p>Neste exemplo, vamos adicionar mais um nó e modificar a RAM e a CPU. Como você pode ver, agora <code>kubectl get elasticsearch</code> mostra 2 nós:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4b139a476b50933d45d99e09479112817964f76a-624x60.png" style="width: 100%;" /><p>O mesmo se aplica ao Kibana:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
  podTemplate:
    spec:
      containers:
        - name: kibana
          env:
            - name: NODE_OPTIONS
              value: "--max-old-space-size=1024"
          resources:
            requests:
              memory: 0.5Gi
              cpu: 0.5
            limits:
              memory: 1Gi
              cpu: 1
EOF</code></pre><p>Podemos ajustar o uso de CPU/RAM do contêiner e também o uso de memória <a href="https://nodejs.org/">do Node.js </a>(<a href="https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-mib">max-old-space-size</a>).</p><p>Lembre-se de que <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/volume-claim-templates">os pedidos de reembolso em grande volume já existentes não podem ser reduzidos</a>. Após aplicar a atualização, o operador fará as alterações com o mínimo de interrupção possível.</p><p>Lembre-se de excluir o cluster quando terminar os testes para evitar custos desnecessários.</p><pre><code>az aks delete --name myAKSAutomaticCluster --resource-group elastic-resource</code></pre><h2>Conclusão</h2><p>Utilizar o Azure AKS Automatic com o ECK oferece uma solução equilibrada para a implementação do Elasticsearch e do Kibana: reduz a complexidade operacional, garante o dimensionamento e as atualizações automatizadas e aproveita a flexibilidade do Kubernetes. Essa abordagem é ideal para equipes que desejam um processo de implantação confiável, repetível e de fácil manutenção, sem precisar gerenciar manualmente cada detalhe da infraestrutura, tornando-se uma escolha prática tanto para ambientes de teste quanto de produção.</p><h2>Próximas etapas</h2><p>Se você quiser saber mais sobre Kubernetes, pode consultar a documentação oficial aqui:</p><ul><li><a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud no Kubernetes | Documentação da Elastic</a></li><li><a href="https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic">Introdução ao Azure Kubernetes Service (AKS) Automático (prévia)</a></li><li><a href="https://azure.github.io/AKS/2024/05/22/aks-automatic">AKS Automatic - Blog de Engenharia da AKS</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando uma sala de imprensa do LLM Agent com protocolo A2A e MCP no Elasticsearch: Parte I]]></title>
            <description><![CDATA[Explore os conceitos do protocolo A2A e do MCP em um exemplo prático de redação, onde agentes especializados em LLM colaboram para pesquisar, escrever, editar e publicar artigos de notícias.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
            <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Introdução</h2><p>Os sistemas atuais baseados em LLM estão evoluindo rapidamente, deixando de ser aplicações de modelo único e se tornando redes complexas onde agentes especializados trabalham juntos para realizar tarefas antes consideradas impossíveis pela computação moderna. À medida que esses sistemas se tornam mais complexos, a infraestrutura que permite a comunicação entre agentes e o acesso a ferramentas passa a ser o foco principal do desenvolvimento. Surgiram duas abordagens complementares para atender a essas necessidades: os protocolos <strong>Agent2Agent (A2A)</strong> para coordenação multiagente e o <strong>Model Context Protocol (MCP)</strong> para acesso padronizado a ferramentas e recursos.</p><p>Compreender quando usar cada um em harmonia com o outro e quando utilizá-los isoladamente pode impactar significativamente a escalabilidade, a facilidade de manutenção e a eficácia de suas aplicações. Este artigo explora os conceitos e implementações do <strong>modelo A2A (Application</strong> -to-Application) no exemplo prático de uma redação digital, onde agentes especializados em LLM (Legal Learning Management) colaboram para pesquisar, escrever, editar e publicar artigos de notícias.</p><p>Um repositório complementar pode ser encontrado <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">aqui</a>, e examinaremos exemplos concretos do A2A em ação perto do final do artigo, na Seção 5.</p><h3>Pré-requisitos</h3><p>O <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">repositório</a> consiste em implementações em Python dos agentes A2A. O Flask fornece um servidor de API, bem como um serviço de mensagens personalizado em Python chamado Event Hub, que encaminha mensagens para registro e atualizações da interface do usuário. Por fim, uma interface de usuário React é fornecida para uso independente dos recursos da sala de imprensa. Tudo está contido em uma imagem Docker para facilitar a implementação. Se você deseja executar os serviços diretamente em sua máquina, precisará garantir que tenha as seguintes tecnologias instaladas:</p><p>Linguagens e ambientes de execução</p><ul><li>Python 13.12 - Linguagem principal de backend</li><li>Node.js 18+ - Interface de usuário React opcional</li></ul><p>Frameworks principais e SDKs:</p><ul><li>SDK A2A 0.3.8 - Coordenação e comunicação de agentes</li><li>SDK Antrópico - Integração com Claude para geração de IA</li><li>Uvicorn - Servidor ASGI para executar agentes</li><li>FastMCP 2.12.5+ - Implementação do servidor MCP</li><li>React 18.2 - Framework de interface de usuário para front-end</li></ul><p>Dados e pesquisa</p><ul><li>Elasticsearch 9.1.1+ - Indexação e pesquisa de artigos</li></ul><p>Implantação do Docker (opcional, mas recomendada)</p><ul><li>Docker 28.5.1+</li></ul><h2>Seção 1: O que é Agent2Agent (A2A)?</h2><h3>Definição e conceitos fundamentais</h3><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><p><strong>Especificação oficial</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p><h3>Origens e evolução</h3><p>O conceito de comunicação Agente para Agente, ou sistemas multiagentes, tem raízes em sistemas distribuídos, microsserviços e pesquisas multiagentes que remontam <a href="https://en.wikipedia.org/wiki/Multi-agent_system">a décadas</a>. Os primeiros trabalhos em inteligência artificial distribuída lançaram as bases para agentes capazes de negociar, coordenar e colaborar. Esses primeiros sistemas eram dedicados a <a href="https://www.jasss.org/5/1/7.html">simulações sociais</a> em larga escala, <a href="https://arxiv.org/html/2410.09403v1">pesquisa acadêmica</a> e <a href="https://www.researchgate.net/publication/334765661_Generation_Expansion_Planning_Considering_Investment_Dynamic_of_Market_Participants_Using_Multi-agent_System">gerenciamento de redes elétricas</a>.</p><p>Com o surgimento da disponibilidade do LLM e a redução do custo de operação, os sistemas multiagentes tornaram-se acessíveis aos mercados &quot;prosumidores&quot;, com o apoio do Google e da comunidade de pesquisa em IA em geral. Agora conhecidos como sistemas Agent2Agent, a adição do protocolo A2A evoluiu para um padrão moderno projetado especificamente para a era de múltiplos modelos de linguagem de grande porte coordenando esforços e tarefas.</p><p>O protocolo A2A garante comunicação e coordenação perfeitas entre os agentes, aplicando padrões e princípios consistentes aos pontos de interação onde os LLMs se conectam e se comunicam. Essa padronização permite que agentes de diferentes desenvolvedores — que utilizam diferentes modelos subjacentes — trabalhem juntos de forma eficaz.</p><p>Os protocolos de comunicação não são novidade e estão amplamente estabelecidos em praticamente todas as transações digitais realizadas na internet. Se você digitou <a href="https://www.elastic.co/search-labs">https://www.elastic.co/search-labs</a> Ao acessar este artigo por meio de um navegador, é muito provável que os protocolos TCP/IP, HTTP e de consulta DNS tenham sido executados, garantindo uma experiência de navegação consistente.</p><h3>Características principais</h3><p>Os sistemas A2A são construídos sobre diversos princípios fundamentais para garantir uma comunicação fluida. Com base nesses princípios, garante-se que diferentes agentes, utilizando diferentes LLMs, frameworks e linguagens de programação, interajam perfeitamente.</p><p>Eis os quatro princípios principais:</p><ul><li><strong>Troca de mensagens</strong>: Os agentes comunicam-se por meio de mensagens estruturadas com propriedades e formatos bem definidos.</li><li><strong>Coordenação</strong>: Os agentes orquestram fluxos de trabalho complexos, delegando tarefas uns aos outros e gerenciando dependências sem bloquear outros agentes.</li><li><strong>Especialização</strong>: Cada agente se concentra em um domínio ou capacidade específica, tornando-se um especialista em sua área e oferecendo a conclusão de tarefas com base nessa habilidade.</li><li><strong>Estado distribuído</strong>: O estado e o conhecimento são distribuídos entre os agentes em vez de centralizados, sendo que os agentes têm a capacidade de atualizar uns aos outros sobre o progresso da tarefa, o estado e os retornos parciais (artefatos).</li></ul><h3>A redação: um exemplo prático</h3><p>Imagine uma redação digital alimentada por agentes de IA, cada um especializado em um aspecto diferente do jornalismo:</p><ul><li><strong>Chefe de Notícias</strong> (coordenador/cliente): Atribui pautas e supervisiona o fluxo de trabalho.</li><li><strong>Agente de reportagem</strong>: Redige artigos com base em pesquisas e entrevistas.</li><li><strong>Agente de Pesquisa</strong>: Reúne fatos, estatísticas e informações de contexto.</li><li><strong>Agente de Arquivo</strong>: Pesquisa artigos históricos e identifica tendências usando o Elasticsearch.</li><li><strong>Agente Editorial</strong>: Analisa artigos quanto à qualidade, estilo e otimização para SEO.</li><li><strong>Agente de Publicação</strong>: Publica artigos aprovados na plataforma do blog via CI/CD</li></ul><p>Esses profissionais não trabalham isoladamente; quando o chefe de jornalismo atribui uma matéria sobre <em>a adoção de energias renováveis</em>, o repórter precisa do pesquisador para coletar as estatísticas, do editor para revisar o rascunho e do editor-chefe para publicar a versão final. Essa coordenação ocorre por meio de protocolos A2A.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" style="width: 100%;" /><h2>Seção 2: Compreendendo a arquitetura A2A</h2><h3>Funções de Agente de Atendimento ao Cliente e Agente Remoto</h3><p>Na arquitetura A2A, os agentes assumem dois papéis principais. O <strong>Agente Cliente</strong> é responsável por formular e comunicar tarefas a outros agentes no sistema. Identifica agentes remotos e suas capacidades, usando essas informações para tomar decisões fundamentadas sobre a delegação de tarefas. O agente do cliente coordena o fluxo de trabalho geral, garantindo que as tarefas sejam distribuídas adequadamente e que o sistema progrida em direção aos seus objetivos.</p><p>O <strong>Agente Remoto</strong>, por outro lado, executa tarefas delegadas pelos clientes. Ela fornece informações ou toma medidas específicas em resposta a solicitações, mas não inicia ações de forma independente. Os agentes remotos também podem se comunicar com outros agentes remotos conforme necessário para cumprir suas responsabilidades atribuídas, criando uma rede colaborativa de capacidades especializadas.</p><p>Em nossa redação, o Chefe de Notícias atua como agente do cliente, enquanto o Repórter, o Pesquisador, o Editor e o Diretor de Publicação são agentes remotos que respondem às solicitações e se coordenam entre si.</p><h3>Principais funcionalidades A2A</h3><p>Os protocolos A2A definem diversas capacidades que permitem a colaboração multiagente:</p><h4>1. Descoberta</h4><p>Os servidores A2A devem anunciar suas funcionalidades para que os clientes saibam quando e como utilizá-las para tarefas específicas. Isso é feito por meio de Cartões de Agente — documentos JSON que descrevem as habilidades, entradas e saídas de um agente. Os cartões de agente são disponibilizados em endpoints consistentes e conhecidos (como o endpoint recomendado <code>/.well-known/agent-card.json</code> ), permitindo que os clientes descubram e consultem as capacidades de um agente antes de iniciar a colaboração.</p><p>Abaixo, segue um exemplo de cartão de agente para o agente de arquivamento personalizado da Elastic, &quot;Archie Archivist&quot;. Note que fornecedores de software como a Elastic hospedam seus agentes A2A e fornecem um URL para acesso:</p><pre><code>{
  "name": "Archie Archivist",
  "description": "Helps find historical news documents in the Elasticsearch Index of archived news articles and content.",
  "url": "https://xxxxxxxxxxxxx-abc123.kb.us-central1.gcp.elastic.cloud/api/agent_builder/a2a/archive-agent",
  "provider": {
    "organization": "Elastic",
    "url": "https://elastic.co"
  },
  "version": "0.1.0",
  "protocolVersion": "0.3.0",
  "preferred_transport": "JSONRPC",
  "documentationURL": "https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "stateTransitionHistory": false
  },
  "skills": [
    {
      "id": "platform.core.search",
      "name": "platform.core.search",
      "description": "A powerful tool for searching and analyzing data within your Elasticsearch cluster.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    },
    {
      "id": "platform.core.index_explorer",
      "name": "platform.core.index_explorer",
      "description": "List relevant indices, aliases and datastreams based on a natural language query.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    }
  ],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"]
}</code></pre><p>Este cartão de agente revela vários aspectos importantes do agente de arquivamento da Elastic. O agente se identifica como &quot;Archie Archivist&quot; e declara claramente seu propósito: ajudar a encontrar documentos de notícias históricas em um índice do Elasticsearch. O cartão especifica o provedor (Elastic) e a versão do protocolo (0.3.0), garantindo a compatibilidade com outros agentes compatíveis com A2A. Mais importante ainda, a matriz <code>skills</code> enumera as capacidades específicas que este agente oferece, incluindo funcionalidades de pesquisa poderosas e exploração inteligente de índices. Cada habilidade define quais modos de entrada e saída ela suporta, permitindo que os clientes entendam exatamente como se comunicar com esse agente. Este agente deriva do serviço Agent Builder da Elastic, que fornece um conjunto de ferramentas nativas com suporte a LLM e endpoints de API para interagir com seu armazenamento de dados, e não apenas para recuperar dados dele. O acesso aos agentes A2A no Elasticsearch pode ser encontrado <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">aqui</a>.</p><h4>2. Negociação</h4><p>Clientes e agentes precisam concordar com os métodos de comunicação — sejam as interações realizadas por meio de texto, formulários, iframes ou até mesmo áudio/vídeo — para garantir a interação adequada do usuário e a troca de dados. Essa negociação ocorre no início da colaboração entre os agentes e estabelece os protocolos que irão reger sua interação ao longo do fluxo de trabalho. Por exemplo, um agente de atendimento ao cliente baseado em voz pode negociar para se comunicar por meio de fluxos de áudio, enquanto um agente de análise de dados pode preferir JSON estruturado. O processo de negociação garante que ambas as partes possam trocar informações de forma eficaz, num formato que se adeque às suas capacidades e às exigências da tarefa em questão.</p><p>As funcionalidades listadas no trecho JSON acima possuem esquemas de entrada e saída; estes definem uma expectativa de como outros agentes devem interagir com este agente.</p><h4>3. Gestão de tarefas e estados</h4><p>Clientes e agentes precisam de mecanismos para comunicar o status das tarefas, alterações e dependências ao longo da execução das mesmas. Isso inclui gerenciar todo o ciclo de vida de uma tarefa, desde a criação e atribuição até as atualizações de progresso e alterações de status. Os status típicos incluem pendente, em andamento, concluído ou reprovado. O sistema também deve rastrear as dependências entre as tarefas para garantir que o trabalho prévio seja concluído antes do início das tarefas dependentes. O tratamento de erros e a lógica de repetição também são componentes essenciais, permitindo que o sistema se recupere de forma adequada de falhas e continue progredindo em direção ao objetivo principal.</p><p>Exemplo de mensagem de tarefa:</p><pre><code>{
  "message_id": "msg_789xyz",
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "researcher_agent",
  "timestamp": "2025-09-30T10:15:00Z",
  "payload": {
    "task_id": "task_456abc",
    "capability": "fact_gathering",
    "parameters": {
      "query": "renewable energy adoption rates in Europe 2024",
      "sources": ["eurostat", "iea", "ember"],
      "depth": "comprehensive"
    },
    "context": {
      "story_id": "story_123",
      "deadline": "2025-09-30T18:00:00Z",
      "priority": "high"
    }
  }
}</code></pre><p>Esta mensagem de tarefa de exemplo demonstra vários aspectos importantes da comunicação A2A.</p><ul><li>A estrutura <strong>da mensagem</strong> inclui metadados como um identificador único da mensagem, o tipo de mensagem que está sendo enviada, a identificação do remetente e do destinatário e um registro de data e hora para rastreamento e depuração.</li><li>A <strong>carga útil</strong> contém as informações reais da tarefa, especificando qual funcionalidade está sendo invocada no agente remoto e fornecendo os parâmetros necessários para executar essa funcionalidade.</li><li>A seção <strong>de contexto</strong> fornece informações adicionais que ajudam o agente receptor a entender o fluxo de trabalho mais amplo, incluindo prazos e níveis de prioridade que orientam a forma como o agente deve alocar seus recursos e programar seu trabalho.</li></ul><h4>4. Colaboração</h4><p>Clientes e agentes <strong>devem</strong> dar suporte a uma interação dinâmica, porém estruturada, permitindo que os agentes solicitem esclarecimentos, informações ou subações do cliente, de outros agentes ou de usuários. Isso cria um ambiente colaborativo onde os agentes podem fazer perguntas de acompanhamento quando as instruções iniciais forem ambíguas, solicitar contexto adicional para tomar melhores decisões, delegar subtarefas a outros agentes com conhecimento mais adequado e fornecer resultados intermediários para feedback antes de prosseguir com a tarefa completa. Essa comunicação multidirecional garante que os agentes não trabalhem isoladamente, mas sim que estejam engajados em um diálogo contínuo que leva a melhores resultados.</p><h3>Comunicação distribuída, ponto a ponto</h3><p>A tecnologia A2A permite a comunicação distribuída, na qual os agentes podem ser hospedados por diferentes organizações, com alguns agentes mantidos internamente, enquanto outros são fornecidos por serviços de terceiros. Esses agentes podem ser executados em diferentes infraestruturas, abrangendo potencialmente vários provedores de nuvem ou centros de dados locais. Eles podem usar diferentes modelos de aprendizado de máquina subjacentes, com alguns agentes baseados em modelos GPT, outros em Claude e outros ainda em alternativas de código aberto. Os agentes podem até operar em diferentes regiões geográficas para cumprir os requisitos de soberania de dados ou reduzir a latência. Apesar dessa diversidade, todos os agentes concordam com um protocolo de comunicação comum para a troca de informações, garantindo a interoperabilidade independentemente dos detalhes de implementação. Essa arquitetura distribuída proporciona flexibilidade na forma como os sistemas são construídos e implantados, permitindo que as organizações combinem os melhores agentes e infraestrutura para suas necessidades específicas.</p><p>Esta é a arquitetura final do aplicativo da redação:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" style="width: 100%;" /><h2>Seção 3: Protocolo de Contexto do Modelo (MCP)</h2><h3>Definição e propósito</h3><p>O Protocolo de Contexto do Modelo (MCP) é um protocolo padronizado desenvolvido pela Anthropic para aprimorar e capacitar um LLM individual com ferramentas, recursos e instruções definidos pelo usuário, entre outras adições suplementares ao código-fonte. O MCP fornece uma interface universal entre modelos de linguagem e os recursos externos necessários para que eles concluam tarefas com eficácia. Este <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">artigo</a> descreve o estado atual do MCP com exemplos de casos de uso, tendências emergentes e a implementação da própria Elastic.</p><h3>Conceitos básicos do MCP</h3><p>O MCP opera em uma arquitetura cliente-servidor com três componentes principais:</p><ul><li><strong>Clientes:</strong> aplicativos (como o Claude Desktop ou aplicativos de IA personalizados) que se conectam aos servidores MCP para acessar suas funcionalidades.</li><li><strong>Servidores</strong>: aplicações que expõem recursos, ferramentas e instruções para modelos de linguagem. Cada servidor é especializado em fornecer acesso a funcionalidades ou fontes de dados específicas.<ul><li><strong>Ferramentas</strong>: funções definidas pelo usuário que os modelos podem invocar para executar ações, como pesquisar bancos de dados, chamar APIs externas ou realizar transformações nos dados.</li><li><strong>Recursos:</strong> fontes de dados que os modelos podem ler, fornecidas com dados dinâmicos ou estáticos e acessadas por meio de padrões de URI (semelhantes a rotas REST).</li><li><strong>Instruções: </strong>modelos de instruções reutilizáveis com variáveis que orientam o modelo na realização de tarefas específicas.</li></ul></li></ul><h3>Padrão de solicitação-resposta</h3><p>O MCP segue um padrão de interação de solicitação-resposta familiar, semelhante às APIs REST. O cliente (LLM) solicita um recurso ou invoca uma ferramenta; em seguida, o servidor MCP processa a solicitação e retorna o resultado, que o LLM utiliza para continuar sua tarefa. Este modelo centralizado com servidores periféricos oferece um padrão de integração mais simples em comparação com a comunicação entre agentes ponto a ponto.</p><h3>MCP na redação</h3><p>Em nosso exemplo de redação, os agentes individuais usam servidores MCP para acessar as ferramentas e os dados de que precisam:</p><ul><li><strong>O Agente de Pesquisa</strong> utiliza:<ul><li>Servidor MCP da API de notícias (acesso a bancos de dados de notícias)</li><li>Servidor MCP de verificação de fatos (verifica alegações em fontes confiáveis)</li><li>Servidor MCP de base de dados académica (artigos académicos e investigação)</li></ul></li><li><strong>O agente repórter</strong> utiliza:<ul><li>Guia de Estilo do Servidor MCP (padrões de redação para redações)</li><li>Servidor MCP de modelos (modelos e formatos de artigos)</li><li>Servidor MCP da Biblioteca de Imagens (fotos e gráficos de banco de imagens)</li></ul></li><li><strong>O Editor Agent</strong> utiliza:<ul><li>Servidor MCP do Verificador Gramatical (ferramentas de qualidade linguística)</li><li>Servidor MCP de Detecção de Plágio (verificação de originalidade)</li><li>Servidor MCP de Análise de SEO (otimização de títulos e palavras-chave)</li></ul></li><li><strong>O Publisher Agent</strong> utiliza:<ul><li>Servidor CMS MCP (API do sistema de gerenciamento de conteúdo)</li><li>Servidor CI/CD MCP (pipeline de implantação)</li><li>Servidor MCP de análise (rastreamento e monitoramento)</li></ul></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" style="width: 100%;" /><h2><br/>Seção 4: comparação de arquiteturas</h2><h3>Quando usar o A2A</h3><p>A arquitetura A2A se destaca em <strong>cenários que exigem colaboração multiagente genuína</strong>. Fluxos de trabalho com várias etapas que exigem coordenação se beneficiam muito do A2A, principalmente quando as tarefas envolvem várias etapas sequenciais ou paralelas, fluxos de trabalho que exigem iteração e refinamento, e processos com pontos de verificação e necessidades de validação. Em nosso exemplo de redação, o fluxo de trabalho da matéria exige que o Repórter escreva, mas pode precisar consultar o Pesquisador se a confiança em certos fatos for baixa, depois passar para o Editor e, finalmente, para o Editor-Chefe.</p><p><strong>A especialização em domínios específicos em diversas áreas</strong> é outro caso de uso importante para o A2A. Quando vários especialistas em diversas áreas são necessários para realizar uma tarefa maior, com cada agente trazendo conhecimento profundo do domínio e capacidades de raciocínio especializadas para diferentes aspectos, o A2A fornece a estrutura de coordenação necessária para fazer essas conexões. A redação exemplifica isso perfeitamente: o pesquisador se especializa na coleta de informações, o repórter na redação e o editor no controle de qualidade — cada um com uma especialização distinta.</p><p>A necessidade de comportamento autônomo dos agentes torna o A2A particularmente valioso. Agentes capazes<strong> de tomar decisões independentes, demonstrar comportamento proativo com base em condições variáveis e se adaptar dinamicamente aos requisitos do fluxo de trabalho</strong> prosperam em uma arquitetura A2A. A escalabilidade horizontal de funções especializadas é outra vantagem fundamental: em vez de ter um único agente que domina todas as tarefas, vários agentes especializados trabalham em coordenação, e várias instâncias do mesmo agente podem lidar com subtarefas de forma assíncrona. Durante a cobertura de notícias de última hora em nossa redação, por exemplo, vários repórteres podem trabalhar simultaneamente em diferentes ângulos da mesma história.</p><p>Por fim, tarefas que exigem colaboração genuína entre múltiplos agentes são ideais para o A2A. Isso inclui mecanismos <a href="https://arxiv.org/abs/2404.18796">de avaliação do LLM como júri</a> , sistemas de consenso e votação, e <strong>resolução colaborativa de problemas onde múltiplas perspectivas são necessárias</strong> para alcançar o melhor resultado.</p><h3>Quando usar o MCP</h3><p>O Protocolo de Contexto de Modelo é ideal para ampliar as capacidades de um único modelo de IA. Quando um único modelo de IA precisa acessar várias ferramentas e fontes de dados, o MCP oferece a solução perfeita, combinando raciocínio centralizado com ferramentas distribuídas e integração de ferramentas simplificada. Em nosso exemplo de redação, o Agente Pesquisador (um modelo) precisa de acesso a múltiplas fontes de dados, incluindo a API de Notícias, serviços de verificação de fatos e bases de dados acadêmicas — todas acessadas por meio de servidores MCP padronizados.</p><p>A integração de ferramentas padronizadas torna-se uma prioridade quando o amplo compartilhamento e a reutilização dessas integrações são importantes. O MCP se destaca aqui com seu ecossistema de servidores MCP pré-configurados, que reduzem significativamente o tempo de desenvolvimento para integrações comuns. Quando simplicidade e facilidade de manutenção são necessárias, os padrões de solicitação-resposta do MCP são familiares aos desenvolvedores, mais fáceis de entender e depurar do que sistemas distribuídos e apresentam menor complexidade operacional.</p><p>Por fim, o MCP costuma ser oferecido por fornecedores de software para facilitar a comunicação remota com seus sistemas. Esses servidores MCP oferecidos pelo provedor reduzem significativamente o tempo de integração e desenvolvimento, ao mesmo tempo que oferecem uma interface padronizada para sistemas proprietários, tornando a integração muito mais simples do que o desenvolvimento de APIs personalizadas.</p><h3>Quando usar ambos (MCP da A2A ❤️)</h3><p>Muitos sistemas sofisticados se beneficiam da combinação de A2A e MCP, conforme observado na <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">documentação da A2A sobre integração com MCP</a>. Sistemas que exigem tanto coordenação quanto padronização são candidatos ideais para uma abordagem híbrida. O A2A lida com a coordenação de agentes e a orquestração de fluxos de trabalho, enquanto o MCP fornece acesso a ferramentas para agentes individuais. Em nosso exemplo de redação, os agentes se coordenam por meio do sistema A2A (atendimento ao usuário), com o fluxo de trabalho indo do repórter para o pesquisador, para o editor e, finalmente, para o editor-chefe. No entanto, cada agente utiliza servidores MCP para suas ferramentas especializadas, criando uma clara separação arquitetural.</p><p>A presença de múltiplos agentes especializados, cada um utilizando o MCP para acesso a ferramentas, representa um padrão comum onde existe uma camada de coordenação de agentes gerenciada pelo A2A e uma camada de acesso a ferramentas gerenciada pelo MCP. Essa clara separação de responsabilidades torna os sistemas mais fáceis de entender e manter.</p><p>Os benefícios de combinar ambas as abordagens são substanciais. Você obtém os benefícios organizacionais dos sistemas multiagentes, incluindo especialização, autonomia e processamento paralelo, ao mesmo tempo que desfruta dos benefícios de padronização e ecossistema do MCP, como integração de ferramentas e acesso a recursos. Existe uma clara separação entre a coordenação de agentes (A2A) e o acesso a recursos (MCP) e, o que é importante, a A2A não é necessária para tarefas menores, como o acesso à API isoladamente — a MCP lida com essas tarefas de forma eficiente, sem a sobrecarga da orquestração multiagente.</p><p><strong>FAQ: A2A vs. MCP - Casos de uso</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Recurso</td><td style="border: 1px solid #ccc; padding: 4px;">Agente para Agente (A2A)</td><td style="border: 1px solid #ccc; padding: 4px;">Protocolo de Contexto do Modelo (MCP)</td><td style="border: 1px solid #ccc; padding: 4px;">Híbrido (A2A + MCP)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Objetivo principal</td><td style="border: 1px solid #ccc; padding: 4px;">Coordenação multiagente: Permite que uma equipe de agentes especializados trabalhe em conjunto em fluxos de trabalho complexos e com várias etapas.</td><td style="border: 1px solid #ccc; padding: 4px;">Aprimoramento para agente único: Amplia a capacidade de um único LLM/Agente com ferramentas, recursos e dados externos.</td><td style="border: 1px solid #ccc; padding: 4px;">Força combinada: A2A gerencia o fluxo de trabalho da equipe, enquanto a MCP fornece ferramentas para cada membro da equipe.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Exemplo de equipe de redação</td><td style="border: 1px solid #ccc; padding: 4px;">A cadeia de fluxo de trabalho: Chefe de Notícias → Repórter → Pesquisador → Editor → Publicador. Esta é a camada de coordenação.</td><td style="border: 1px solid #ccc; padding: 4px;">Ferramentas do agente individual: O Agente Repórter acessa o servidor de guia de estilo e o servidor de modelos (via MCP). Esta é a camada de acesso à ferramenta.</td><td style="border: 1px solid #ccc; padding: 4px;">O sistema completo: o repórter coordena com o editor (A2A) e utiliza o servidor MCP da biblioteca de imagens para encontrar uma imagem para a matéria.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Quando usar qual</td><td style="border: 1px solid #ccc; padding: 4px;">Quando você precisa de colaboração genuína, iteração e aprimoramento, ou de conhecimento especializado dividido entre vários agentes.</td><td style="border: 1px solid #ccc; padding: 4px;">Quando um único agente precisa acessar várias ferramentas e fontes de dados ou requer integração padronizada com sistemas proprietários.</td><td style="border: 1px solid #ccc; padding: 4px;">Quando você precisa dos benefícios organizacionais dos sistemas multiagentes e dos benefícios de padronização e ecossistema do MCP.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Benefício principal</td><td style="border: 1px solid #ccc; padding: 4px;">Autonomia e escalabilidade: Os agentes podem tomar decisões independentes e o sistema permite a escalabilidade horizontal de funções especializadas.</td><td style="border: 1px solid #ccc; padding: 4px;">Simplicidade e padronização: Mais fácil de depurar e manter devido ao raciocínio centralizado, além de fornecer uma interface universal para recursos.</td><td style="border: 1px solid #ccc; padding: 4px;">Separação clara de responsabilidades: torna o sistema mais fácil de entender: A2A = trabalho em equipe, MCP = acesso à ferramenta.</td>
                    </tr>
              </tbody>
            </table>
          <img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" style="width: 100%;" /><h2>Conclusão</h2><p>Esta é a primeira parte de um artigo em duas seções que aborda a implementação de agentes baseados em A2A, reforçados com servidores MCP para fornecer suporte e acesso externo a dados e ferramentas. A próxima parte explorará o código real para demonstrar como eles funcionam em conjunto, simulando as atividades de uma redação online. Embora ambas as estruturas sejam extremamente capazes e flexíveis por si só, você verá o quanto elas se complementam quando trabalham em conjunto.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Sabe, para contexto - Parte I: A evolução da busca híbrida e da engenharia de contexto]]></title>
            <description><![CDATA[Explore como a busca híbrida e a engenharia de contexto evoluíram a partir de fundamentos lexicais para viabilizar a próxima geração de fluxos de trabalho de IA com agentes.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/context-engineering-hybrid-search-evolution-agentic-ai</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/context-engineering-hybrid-search-evolution-agentic-ai</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Woody Walton]]></dc:creator>
            <pubDate>Wed, 12 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7e59a0671aa9ea32d68188a693936a66ebf48625-1000x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Nosso novíssimo mundo de IA agente</h2><p>Como muitos de nós, me sinto ao mesmo tempo entusiasmado e surpreso com a velocidade com que as capacidades da IA estão evoluindo. Vimos pela primeira vez os grandes modelos de linguagem (LLMs) e a busca vetorial nos lançarem na revolução semântica, onde não precisávamos mais ficar procurando coisas com palavras-chave. Em seguida, os LLMs nos mostraram novas maneiras de interagir com nossos dados, usando interfaces de bate-papo para transformar solicitações em linguagem natural em respostas que destilam vastas bases de conhecimento em resumos facilmente assimiláveis. Nós agora (já!) Possuem os primórdios da lógica automatizada orientada por LLM na forma de fluxos de trabalho de &quot;IA agente&quot; que podem compreender semanticamente uma solicitação recebida, raciocinar sobre as etapas a serem seguidas e, em seguida, escolher entre as ferramentas disponíveis para executar iterativamente ações para atingir esses objetivos.</p><p>A promessa da IA agente está nos forçando a evoluir, deixando de usar principalmente a &quot;engenharia de prompts&quot; para moldar nossas interações generativas de IA, e passando a nos concentrar em como podemos ajudar as ferramentas agentes a obter as informações adicionais mais relevantes e eficientes que o LLM precisa considerar ao gerar suas respostas — a &quot;engenharia de contexto&quot; é a próxima fronteira. A busca híbrida é, de longe, o meio mais poderoso e flexível para revelar contexto relevante, e a plataforma Search AI da Elastic abre uma nova maneira de aproveitar os dados a serviço da engenharia de contexto. Neste artigo, discutiremos como os Modelos de Aprendizagem Baseados em Liderança (LLMs) transformaram o mundo da recuperação de informação sob duas perspectivas e, em seguida, como eles podem trabalhar em conjunto para alcançar melhores resultados. Há muito terreno a percorrer…</p><h2>Parte I: Como os LLMs mudaram a busca</h2><p>Vamos começar pela perspectiva de como os LLMs (mestrados em direito) mudaram a forma como acessamos e recuperamos informações.</p><h3>Nosso legado lexical</h3><p>Há muito tempo que vivemos num mundo de busca lexical um tanto limitado (ou quase, da melhor forma possível). A busca é a primeira ferramenta que utilizamos ao pesquisar ou iniciar um novo projeto e, até recentemente, dependia de nós formular nossas consultas de uma maneira que um mecanismo de busca lexical entendesse. A busca lexical baseia-se na correspondência de algum tipo de termo de consulta com palavras-chave encontradas em um conjunto de documentos — independentemente de o conteúdo ser estruturado ou não estruturado. Para que uma busca lexical retorne um documento como resultado, ele precisa ter correspondido àquela palavra-chave (ou ter um vocabulário controlado, como uma lista de sinônimos ou um dicionário, para fazer a conexão conceitual para nós).</p><pre><code>POST my-index/_search
{
  "size": 10,
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
}</code></pre><p><em>Um exemplo de <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query"></a> consulta lexical de correspondência múltipla</em></p><p>Ao menos os mecanismos de busca têm a capacidade de retornar resultados com uma pontuação de relevância. Os mecanismos de busca oferecem uma ampla gama de opções de sintaxe de consulta para segmentar dados indexados de forma eficaz, além de algoritmos de relevância integrados que classificam os resultados de acordo com a intenção da sintaxe de consulta do usuário. Os mecanismos de busca se beneficiam de décadas de avanços em algoritmos de classificação por relevância, o que os torna uma plataforma eficiente de recuperação de dados, capaz de fornecer resultados pontuados e classificados de acordo com sua relevância para a consulta. Bancos de dados e outros sistemas que usam SQL como principal método para recuperar dados estão em desvantagem nesse aspecto: não existe o conceito de relevância em uma consulta de banco de dados; o máximo que podem fazer é classificar os resultados alfabeticamente ou numericamente. A boa notícia é que você obterá todos os resultados (recall) com essas palavras-chave, mas eles não estarão necessariamente em uma ordem útil em relação ao <em>motivo pelo qual</em> você os solicitou (precisão). Esse é um ponto importante, como veremos em breve…</p><h3>Entre o dragão (semântico)</h3><p>O potencial das representações vetoriais de informações como alternativa à busca por palavras-chave vem sendo pesquisado há <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">bastante tempo</a>. Os vetores são muito promissores porque nos libertam do modo de correspondência de conteúdo baseado apenas em palavras-chave — como são representações numéricas de termos e pesos, os vetores permitem que os conceitos sejam matematicamente próximos com base na compreensão do modelo de linguagem sobre como os termos se relacionam entre si no domínio de treinamento. A longa demora na busca por vetores de propósito geral se devia ao fato de os modelos serem, em sua maioria, limitados a domínios específicos; eles simplesmente não eram grandes o suficiente para compreender adequadamente os muitos conceitos diferentes que um termo poderia representar em diferentes contextos.</p><p>Foi somente com o surgimento dos Modelos de Linguagem de Grande Porte (LLMs, na sigla em inglês), há alguns anos, e sua capacidade de serem treinados com quantidades muito maiores de dados (usando <a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">transformadores</a> e <a href="https://en.wikipedia.org/wiki/Attention_(machine_learning)">atenção</a>), que a busca vetorial se tornou viável — o tamanho e a profundidade dos LLMs finalmente permitiram que os vetores armazenassem nuances suficientes para, de fato, capturar o significado semântico. Esse aumento repentino na profundidade de compreensão permitiu que os LLMs (Learning Language Machines) passassem a desempenhar diversas funções de processamento de linguagem natural (PLN) que antes estavam bloqueadas, sendo talvez a mais impactante a capacidade de inferir o próximo termo mais provável em uma sequência, dado o contexto do que já estava presente na sequência. A inferência é o processo que confere à IA generativa sua capacidade quase humana de produzir texto. O texto gerado por IA é baseado na compreensão do LLM sobre como os termos se relacionam em seus dados de treinamento e também utiliza a formulação da solicitação para diferenciar os diversos contextos em que os termos podem aparecer.</p><p>Por mais mágica que seja a IA generativa, os LLMs <em>têm</em> limitações que causam erros de qualidade e precisão, comumente chamados de alucinações. As alucinações ocorrem quando o profissional de saúde mental não tem acesso à informação (ou não é guiado ao contexto correto) para basear sua resposta na verdade, então, na tentativa de ser prestativo, ele gera uma resposta confiante e plausível que, na verdade, é inventada. Parte da causa é que, embora os LLMs aprendam o uso da linguagem em grandes domínios de informações diversas, eles precisam interromper o treinamento em um determinado momento, portanto, há um fator de temporalidade em sua compreensão — o que significa que o modelo só pode saber o que era preciso até o momento em que o treinamento foi interrompido. Outro fator que contribui para as alucinações é que o modelo geralmente desconhece dados privados (dados não disponíveis na internet pública), e isso é especialmente significativo quando esses dados contêm termos e nomenclatura específicos.</p><h3>Bancos de dados vetoriais</h3><p>Os LLMs vetorizam o conteúdo para seu espaço de modelo usando uma técnica chamada incorporação de texto, que se refere à <a href="https://www.elastic.co/search-labs/blog/hybrid-search-multiple-embeddings">incorporação</a> ou mapeamento do significado semântico do conteúdo na visão de mundo do modelo com base no treinamento que ele recebeu. Existem algumas etapas envolvidas na preparação e no processamento de conteúdo para incorporação, incluindo <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">a segmentação</a> e a tokenização (e <a href="https://www.kaggle.com/code/danishmahdi/subword-tokenization-bpe-wordpiece-and-unigram">a tokenização de subpalavras</a>). O resultado é tipicamente um conjunto de vetores densos que representam a compreensão do modelo sobre o significado daquele trecho de conteúdo dentro de seu espaço vetorial. O chunking é um processo impreciso que visa ajustar o conteúdo às limitações de processamento de um modelo para gerar embeddings, tentando também agrupar textos relacionados em um chunk usando construções semânticas como indicadores de sentença e parágrafo.</p><p>A necessidade de fragmentação pode gerar alguma perda semântica em um documento incorporado, porque os fragmentos individuais não estão totalmente associados a outros fragmentos do mesmo documento. A opacidade inerente das redes neurais pode agravar essa perda de informação — um modelo de aprendizagem linear é verdadeiramente uma “caixa preta”, onde as conexões entre termos e conceitos feitas durante o treinamento não são determinísticas e não podem ser interpretadas por humanos. Isso acarreta problemas de explicabilidade, repetibilidade, viés inconsciente e, potencialmente, perda de confiança e precisão. No entanto, a capacidade de conectar ideias semanticamente, de não estar vinculado a palavras-chave específicas ao fazer buscas, é extremamente poderosa:</p><pre><code>POST my-index/_search 
{
  "size": 10, 
  "query": {
    "semantic": {
      "query": "machine learning applications",
      "field": "semantic-content-field"
    }
  }
} </code></pre><p><em>Um exemplo <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-semantic-query">de</a> consulta semântica</em></p><p>Há ainda outra questão a considerar em relação às bases de dados vetoriais: elas não são motores de busca, são bases de dados! Quando uma <a href="https://www.elastic.co/search-labs/blog/introduction-to-vector-search">busca por similaridade vetorial</a> é realizada, os termos da consulta são codificados para encontrar um conjunto de coordenadas (de incorporação) dentro do espaço vetorial do modelo. Essas coordenadas são então usadas como o alvo para encontrar os documentos que são os &quot;vizinhos mais próximos&quot; do alvo — o que significa que a classificação de um documento (ou sua posição nos resultados) é determinada pela <em>distância</em> de similaridade calculada entre as coordenadas desse documento e as coordenadas da consulta. Em que direção a classificação deve ter prioridade? Qual dos contextos possíveis está mais próximo da intenção do usuário? A imagem que me vem à mente é uma cena do filme <a href="https://www.youtube.com/watch?v=x3h7xz558EY&amp;start=3&amp;end=86">Stargate</a>, onde temos os seis pontos de coordenadas que se cruzam para nos indicar o destino (o alvo), mas não conseguimos chegar lá sem conhecer o &quot;sétimo símbolo&quot; - as coordenadas do ponto de partida que representam a intenção subjetiva do usuário. Assim, em vez de a classificação relativa dos vetores ser baseada em uma esfera de similaridade cada vez maior e indiferenciada, ao considerarmos a intenção subjetiva da consulta por meio de sintaxe expressiva e pontuação de relevância, podemos obter algo semelhante a um <em>cilindro</em> de relevância subjetiva graduada.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ddfaae0c1496d08d7d30419e6d2aeaeacfc0ea2-1600x544.png" style="width: 100%;" /><p>As capacidades de inferência de um LLM podem ajudar a identificar o contexto mais provável <em>para</em> a consulta, mas o problema é que <em>, sem essa ajuda,</em> as coordenadas da consulta recebida <em>só</em> podem ser determinadas pela forma como o modelo foi originalmente treinado.</p><p>De certa forma, pode-se dizer que a similaridade vetorial vai ao extremo oposto da correspondência estrita por palavras-chave — sua força reside na capacidade de superar os problemas de incompatibilidade de termos, mas <a href="https://medium.com/data-science/vector-embeddings-are-lossy-heres-what-to-do-about-it-4f9a8ee58bb7">quase em excesso</a>: os modelos de similaridade de palavras tendem a unificar conceitos relacionados em vez de diferenciá-los. A similaridade vetorial melhora nossa capacidade de combinar conteúdo semanticamente, mas não garante precisão, pois pode ignorar palavras-chave exatas e detalhes específicos que não são suficientemente desambiguados pelo modelo. A busca por similaridade vetorial é poderosa por si só, mas precisamos de maneiras de correlacionar os resultados que obtemos de um banco de dados vetorial com os resultados de outros métodos de recuperação.</p><h3>Técnicas de reclassificação</h3><p>Agora é um bom momento para mencionar uma técnica geral chamada reclassificação, que reavalia ou normaliza os conjuntos de resultados para uma ordem de classificação unificada. A necessidade de reclassificação pode ser devida a resultados de múltiplas fontes ou métodos de recuperação que possuem mecanismos de classificação/pontuação diferentes (ou nenhum, como no caso do SQL!), ou a reclassificação pode ser usada para alinhar semanticamente os resultados de fontes não semânticas à consulta do usuário. A reclassificação é uma operação de segunda etapa, ou seja, um conjunto de resultados que foram coletados por algum método <em>de recuperação inicial</em> (ou seja, Em seguida, os métodos de busca (SQL, busca lexical, busca vetorial) são reordenados com um método de pontuação diferente.</p><p>Existem diversas abordagens disponíveis, incluindo <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr">Learning-To-Rank (LTR)</a> e <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">Reciprocal Rank Fusion (RRF)</a> — o LTR é útil para capturar características dos resultados de pesquisa (curtidas, avaliações, cliques, etc.) e usá-las para pontuar e impulsionar ou influenciar os resultados. O RRF é perfeito para mesclar resultados retornados de diferentes modalidades de consulta (por exemplo, pesquisas lexicais e em bancos de dados vetoriais) juntas em uma única lista de resultados. O Elastic também oferece a flexibilidade de ajustar as pontuações usando métodos <a href="https://www.elastic.co/search-labs/blog/linear-retriever-hybrid-search">de reclassificação linear</a> .</p><p>Uma das técnicas de reclassificação mais eficazes, no entanto, é <a href="https://www.elastic.co/docs/solutions/search/ranking/semantic-reranking">a reclassificação semântica</a>, que utiliza a compreensão semântica de um modelo de aprendizado de máquina para analisar os vetores de incorporação da consulta e dos resultados em conjunto e, em seguida, aplicar a pontuação/repontuação de relevância para determinar a ordem final. A reclassificação semântica requer, obviamente, uma conexão com um modelo de reclassificação, e o Elasticsearch fornece uma <a href="https://www.elastic.co/docs/api/doc/elasticsearch/group/endpoint-inference">API de Inferência</a> que permite criar endpoints <strong>de reclassificação</strong> que utilizam modelos integrados (<a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-rerank">Elastic Rerank</a>), modelos de terceiros <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/eland/machine-learning">importados</a> ou serviços hospedados externamente, como <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-cohere">Cohere</a> ou <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-googlevertexai">Google Vertex AI</a>. Em seguida, você pode realizar a reclassificação por meio da sintaxe de abstração de consulta <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">do recuperador</a> :</p><pre><code>POST my-index/_search 
{
  "size": 10,
  "retriever": {
    "text_similarity_reranker": {
      "retriever": {
        "rrf": {
          "retrievers": [
            {
              "standard": {
                "query": {
                  "multi_match": {
                    "query": "machine learning applications",
                    "fields": ["title", "content"]
                  }
                }
              }
            },
            {
              "knn": {
                "field": "semantic-content-field",
                "k": 10,
                "num_candidates": 100,
                "query_vector_builder": {
                  "text_embedding": {
                    "model_id": "my-text-embedding-model",
                    "model_text": "machine learning applications"
                  }
                }
              }
            }
          ],
          "rank_window_size": 50,
          "rank_constant": 20
        }
      }
    },
    "field": "content",
    "inference_id": "my-reranker",
    "inference_text": "machine learning applications",
    "rank_window_size": 20
  }
}</code></pre><p><em>Um exemplo de operação de reclassificação de recuperação em múltiplos estágios</em></p><p>Parece ótimo, não é? Podemos realizar uma reclassificação de resultados de fontes distintas e chegar perto de uma compreensão semântica de todos os tipos de conteúdo... A reclassificação semântica pode ser dispendiosa tanto em termos computacionais quanto de tempo de processamento, e por isso, só pode ser feita de forma viável em um número limitado de resultados, o que significa que <em>a forma como</em> esses resultados iniciais são obtidos é importante.</p><h3>O método de recuperação de contexto é importante.</h3><p>A intenção subjetiva é um fator importante para determinar a precisão de um resultado e avaliar sua relevância. Sem a possibilidade de considerar a intenção do usuário ao realizar a consulta (expressa por meio de sintaxe flexível ou por reclassificação em um segundo estágio), só podemos selecionar entre os contextos existentes já codificados no espaço do modelo. Normalmente, lidamos com essa falta de contexto por meio de técnicas como <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation">a Geração de Aumento de Recuperação (RAG)</a>. O RAG funciona alterando as coordenadas da consulta ao incluir termos relacionados adicionais retornados de uma pré-consulta para obter dados contextualmente relevantes. Isso torna o mecanismo que fornece esse contexto adicional e <em>seu</em> método inicial de recuperação ainda mais importantes para a precisão do contexto!</p><p>Vamos analisar os diferentes métodos de recuperação de contexto e como eles podem ajudar ou prejudicar uma operação RAG:</p><ul><li><strong>A recuperação de pesquisa híbrida sem um mecanismo de busca ainda carece de relevância subjetiva.</strong> Se a plataforma que fornece o RAG for baseada principalmente em SQL (o que inclui a maioria das plataformas de &quot;data lake&quot;), ela não terá pontuação de relevância na fase inicial de recuperação. Muitas plataformas de data lake oferecem sua própria versão de recuperação híbrida (não busca), geralmente combinando técnicas de reclassificação como reclassificação semântica e RRF em seus resultados de recuperação baseados em SQL e em bancos de dados vetoriais. Uma simples ordenação é obviamente insuficiente para uma classificação subjetiva, mas mesmo quando usada como base para uma operação de reclassificação semântica de segundo estágio, o SQL como recuperação de primeiro estágio torna-se um problema quando a reclassificação semântica é realizada apenas nos &quot;k melhores&quot; resultados — sem alguma forma de pontuar os resultados na recuperação, que garantia temos de que os <em>melhores</em> resultados estão realmente entre os primeiros resultados?</li><li><strong>A similaridade vetorial por si só não é suficiente para o RAG</strong>. Na verdade, isso se deve a uma série de problemas que se acumulam: a perda de dados inerente ao processo de incorporação, juntamente com métodos ingênuos de segmentação, a forma como a similaridade é calculada e a ausência crucial do componente de intenção subjetiva. Um dos principais objetivos do RAG é fundamentar as interações da IA generativa na verdade objetiva, tanto para evitar alucinações quanto para informar o LLM sobre informações privadas que ele desconhecia durante o treinamento. Podemos usar o contexto adicional fornecido pelo RAG para restringir e direcionar os LLMs a considerarem as conexões e os detalhes que sabemos serem mais importantes para responder à questão em análise. Para isso, precisamos usar <em>abordagens</em> semânticas e lexicais.</li><li><strong>RAG baseado em grep/regex de arquivo.</strong> Há <a href="https://www.nicolasbustamante.com/p/the-rag-obituary-killed-by-agents">setores</a> do universo da IA agente que apontam para o uso de janelas de contexto vastamente ampliadas que acessam arquivos locais via grep e regex para RAG (Random Access Groups - Grupos de Acesso Aleatório) em vez de plataformas de recuperação externas. A ideia é que, com uma janela contextual muito maior disponível, os profissionais de Letras e Literatura (LLMs) poderão fazer conexões conceituais dentro de seu próprio espaço de pensamento, em vez de depender de fragmentos isolados e de múltiplos métodos/plataformas de recuperação de informações para coletar informações relevantes. Embora seja verdade, em teoria, que ter um documento inteiro forneça uma visão mais completa do que segmentos de um documento, isso só funciona em domínios de dados pequenos (ou, por exemplo, ao fornecer arquivos para <a href="https://en.wikipedia.org/wiki/Vibe_coding">vibecoding</a>) e, mesmo assim, o método de recuperação inicial é uma varredura de todos os documentos com correspondência apenas por palavra-chave.</li></ul><p><strong>A busca é mais do que a recuperação de informações.</strong></p><p>Os mecanismos de busca são projetados especificamente para tornar as consultas o mais rápidas e flexíveis possível. Internamente, utilizam estruturas de dados especializadas para armazenar e recuperar diferentes tipos de dados de maneiras que atendam às necessidades específicas de cada tipo de dado. O Elasticsearch oferece armazenamento e consulta otimizados para praticamente todos os tipos de dados, incluindo busca lexical em texto completo/não estruturado (correspondência, frase, proximidade, correspondência múltipla), correspondência e filtragem rápidas por palavra-chave (correspondência exata), intervalos numéricos, datas, endereços IP, e é muito flexível na forma como armazena estruturas de documentos (por exemplo, documentos aninhados ou achatados). O Elasticsearch também é um banco de dados vetorial nativo que pode armazenar e consultar tipos de vetores esparsos e densos, e continuamos a explorar maneiras inovadoras (por exemplo, <a href="https://www.elastic.co/search-labs/blog/better-binary-quantization-lucene-elasticsearch">Better Binary Quantization (BBQ)</a> e <a href="https://www.elastic.co/search-labs/blog/diskbbq-elasticsearch-introduction">DiskBBQ</a>) para manter a fidelidade da pesquisa, ao mesmo tempo que melhoramos a velocidade, a escalabilidade e os custos associados ao conteúdo vetorizado. A plataforma Elasticsearch também oferece resiliência de dados e alta disponibilidade integradas, e inclui recursos de gerenciamento do ciclo de vida dos dados, como <a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore/searchable-snapshots">Snapshots pesquisáveis</a> , que permitem manter dados acessados com pouca frequência ou com retenção de longo prazo em armazenamento de objetos econômico — mas ainda totalmente pesquisáveis.</p><h3>A busca híbrida oferece o melhor de todos os mundos.</h3><p><a href="https://www.elastic.co/what-is/hybrid-search">Busca híbrida</a> (e não apenas recuperação híbrida!) Combina os pontos fortes da busca lexical tradicional com a compreensão semântica dos Modelos de Aprendizagem Lógica (LLMs) e da busca por similaridade vetorial. Essa sinergia permite direcionar resultados altamente relevantes na fase <em>de recuperação</em> por meio de qualquer uma das opções flexíveis de sintaxe de consulta que um mecanismo de busca oferece: opções de sintaxe orientadas por intenção e pontuação de relevância, recuperação de dados multimodais, filtragem, agregações e direcionamento. Com sintaxes de busca como <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> e <a href="https://www.elastic.co/docs/solutions/search/retrievers-overview">mecanismos de recuperação</a> em múltiplos estágios, podemos combinar de forma flexível a busca tradicional com a busca semântica, filtros e múltiplas técnicas de reclassificação, tudo em uma única requisição.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f25180cb430414b99ae553d3b8eb161dbccea4d4-1920x1080.png" style="width: 100%;" /><p>Uma das maiores vantagens da pesquisa híbrida é que suas consultas podem usar sintaxe especializada para vários tipos de dados diferentes simultaneamente. Essas diferentes sintaxes de consulta podem ser usadas não apenas para <em>encontrar</em> resultados, mas também como filtros ou agregações <em>nos</em> resultados. Por exemplo, um dos tipos de consulta mais comuns que frequentemente é combinado com outras sintaxes é <a href="https://www.elastic.co/docs/explore-analyze/geospatial-analysis">a análise geoespacial</a>. Você pode realizar ações como consultar resultados que possuam coordenadas geográficas dentro de uma distância específica de um ponto, solicitar agregações de seus resultados por região ou ainda agregações para rastrear e alertar sobre movimentos de entrada e saída de uma zona. Com a pesquisa híbrida, você tem a flexibilidade de combinar sintaxes para direcionar os resultados da maneira mais precisa, recuperando o conteúdo mais próximo do seu contexto.</p><h2>Intervalo</h2><p>Esta primeira parte conta a história de como a busca vetorial mudou a forma como conseguimos recuperar dados e prepara o terreno para as mudanças que os Modelos de Aprendizagem Baseados em Lógica (LLMs) trouxeram aos mecanismos de consulta que usamos para interagir com os dados. Vamos fingir que tivemos que dividir isso em várias partes para que os LLMs pudessem entender sem perder o contexto… ;-) Vamos aprender mais sobre <em>por que isso é importante</em> na <a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">Parte II: IA Agêntica e a necessidade de engenharia de contexto</a>, e na Parte III, retornaremos à nossa discussão sobre busca híbrida.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Configurando o particionamento recursivo para documentos estruturados no Elasticsearch]]></title>
            <description><![CDATA[Aprenda como configurar o particionamento recursivo no Elasticsearch com tamanho de partição, grupos de separadores e listas de separadores personalizadas para indexação ideal de documentos estruturados.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/recursive-chunking-structured-documents-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/recursive-chunking-structured-documents-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Daniel Rubinstein]]></dc:creator>
            <pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/591872e31880768ca927507654a621addc0d124d-1600x960.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Desde a versão 8.16, os usuários podem configurar a estratégia de fragmentação usada ao importar documentos longos para campos de texto semântico. A partir da versão 9.1 / 8.19, introduzimos uma nova estratégia de fragmentação recursiva configurável que utiliza uma lista de expressões regulares para dividir o documento em partes. O objetivo do chunking é dividir um documento longo em seções que englobem conteúdo relacionado. Nossas estratégias atuais dividem o texto em uma granularidade de palavras/frases, mas documentos escritos em formatos estruturados (ex.: O Markdown) geralmente contém conteúdo relacionado dentro de seções que são definidas por algumas strings separadoras (ex. cabeçalhos). Para esses tipos de documentos, estamos introduzindo a estratégia de fragmentação recursiva para aproveitar o formato de documentos estruturados e criar fragmentos melhores!</p><h2>O que é fragmentação recursiva?</h2><p>O particionamento recursivo percorrerá uma lista de seções fornecidas, separando padrões para dividir progressivamente um documento em segmentos menores até atingir o tamanho máximo desejado.</p><h3>Como configuro o chunking recursivo?</h3><p>A seguir, estão os valores configuráveis fornecidos pelo usuário para o particionamento recursivo:</p><ul><li>(obrigatório) <code>max_chunk_size</code>: O número máximo de palavras em um bloco.</li><li>Qualquer uma das seguintes opções:<ul><li><code>separators</code>Uma lista de padrões de strings de expressão regular que serão usados para dividir o documento em partes.</li><li><code>separator_group</code>: Uma string que será mapeada para uma lista padrão de separadores definida pela Elastic para uso em tipos específicos de documentos. Atualmente, <code>markdown</code> e <code>plaintext</code> estão disponíveis.</li></ul></li></ul><h3>Como funciona o particionamento recursivo?</h3><p>O processo de fragmentação recursiva, dado um documento de entrada, um <code>max_chunk_size</code> (medido em palavras) e uma lista de strings separadoras, é o seguinte:</p><ol><li>Se o documento de entrada já estiver dentro do tamanho máximo do bloco, retorne um único bloco que abranja toda a entrada.</li><li>Divida o texto em partes potenciais com base nas ocorrências do separador. Para cada bloco potencial:<ol><li>Se o fragmento em potencial estiver dentro do tamanho máximo permitido, adicione-o à lista de fragmentos a serem retornados ao usuário.</li><li>Caso contrário, repita a partir do passo 2, usando apenas o texto do possível bloco e dividindo-o usando o próximo separador da lista. Se não houver mais separadores para tentar, recorra à segmentação baseada em frases.</li></ol></li></ol><h2>Exemplos de configuração de fragmentação recursiva</h2><p>Além do tamanho do bloco, a principal configuração para o particionamento recursivo é selecionar quais separadores devem ser usados para dividir seus documentos. Se você não sabe por onde começar, o Elasticsearch oferece alguns grupos de separadores padrão que podem ser usados para casos de uso comuns.</p><h3>Utilizando grupos separadores</h3><p>Para utilizar um grupo separador, basta fornecer o nome do grupo que você deseja usar ao configurar as opções de fragmentação. Por exemplo:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separator_group": "plaintext"
}</code></pre><p>Isso lhe dará uma estratégia de fragmentação recursiva que utiliza a lista de separadores <code>[&quot;(?&lt;!\\n)\\n\\n(?!\\n)&quot;, &quot;(?&lt;!\\n)\\n(?!\\n)&quot;)]</code>. Isso funciona bem para aplicações genéricas de texto simples, dividindo o texto em dois caracteres de nova linha, seguidos por um caractere de nova linha.</p><p>Também oferecemos um grupo separador <code>markdown</code> que utilizará a lista de separadores:</p><pre><code>[
"\n# ",
       "\n## ",
       "\n### ",
       "\n#### ",
       "\n##### ",
       "\n###### ",
       "\n^(?!\\s*$).*\\n-{1,}\\n",
       "\n^(?!\\s*$).*\\n={1,}\\n"
]</code></pre><p>Esta lista de separadores funcionará bem para casos de uso gerais de Markdown, dividindo o texto em cada um dos 6 níveis de título e nos caracteres de quebra de seção.</p><p>Ao criar um recurso (ponto de extremidade de inferência/campo de texto semântico), a lista de separadores correspondentes ao grupo de separadores no momento será armazenada em suas configurações. Se o grupo separador for atualizado posteriormente, isso não alterará o comportamento dos seus recursos já criados.</p><h3>Utilizando uma lista separadora personalizada</h3><p>Se um dos grupos de separadores predefinidos não for adequado ao seu caso de uso, você pode definir uma lista personalizada de separadores que atenda às suas necessidades. Observe que expressões regulares podem ser fornecidas dentro da lista de separadores. Segue abaixo um exemplo de configurações de fragmentação configuradas com separadores personalizados:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n\n", "\n", "<my-custom-separator>"]
}</code></pre><p>A estratégia de fragmentação acima dividirá em 2 caracteres de nova linha, seguidos por 1 caractere de nova linha e, por último, em uma string <code>“&lt;my-custom-separator&gt;”</code>.</p><h2>Um exemplo de fragmentação recursiva em ação.</h2><p>Vejamos um exemplo de fragmentação recursiva em ação. Neste exemplo, usaremos as seguintes configurações de fragmentação com uma lista personalizada de separadores que dividem um documento Markdown usando os dois níveis de cabeçalho superiores:</p><pre><code>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n# ", "\n## "]
}</code></pre><p>Vamos analisar um documento Markdown simples, sem divisões em partes (unchunked):</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a5507f4a1288065097231548e5b18e240508785-1302x1446.png" style="width: 100%;" /><p>Agora vamos usar as configurações de fragmentação definidas acima para dividir o documento em partes:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a3313c4c40ff39b8dbcdd7c4878c723f088e6c1a-1600x1187.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/79a2921943191ea631df94c9d465818ec8d3e738-1600x1206.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/459e695cce7540267422396b9a62ff4ad35f61db-1600x1260.png" style="width: 100%;" /><p>Nota: A quebra de linha no final de cada bloco (exceto o Bloco 3) não está destacada, mas está incluída dentro dos limites reais do bloco.</p><h3>Comece a usar o chunking recursivo hoje mesmo!</h3><p>Para obter mais informações sobre como utilizar este recurso, consulte a documentação sobre <span class="unknown__pt__mark__88e241676926">como configurar as definições de fragmentação</span>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apresentando a interface de usuário de regras de consulta do Elasticsearch no Kibana.]]></title>
            <description><![CDATA[Aprenda a usar a interface de regras de consulta do Elasticsearch para adicionar ou excluir documentos de consultas de pesquisa usando conjuntos de regras personalizáveis no Kibana, sem afetar o ranking orgânico.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-query-rules-ui-introduction</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-query-rules-ui-introduction</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jhon Guzmán]]></dc:creator>
            <pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1fb10bd31c509cc9b9bb4f71f49970f140e6c36f-1600x945.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A função de um mecanismo de busca é retornar resultados relevantes. No entanto, existem necessidades comerciais que vão além disso — como destacar promoções, priorizar produtos sazonais ou exibir itens patrocinados — e os desenvolvedores nem sempre podem fazer isso na consulta de pesquisa.</p><p>Além disso, esses casos de uso geralmente são sensíveis ao tempo, e passar pelas etapas típicas de desenvolvimento (criar uma ramificação de código e depois esperar por um novo lançamento) é um processo demorado.</p><p>E se pudéssemos realizar todo esse processo com apenas uma chamada de API, ou melhor ainda, com apenas alguns cliques no Kibana?</p><h2>Interface do usuário de regras de consulta</h2><p>O Elasticsearch 8.10 introduziu <a href="https://www.elastic.co/blog/introducing-query-rules-elasticsearch-8-10"><strong>as Regras de Consulta</strong></a> e <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrievers/rule-retriever"><strong>o Recuperador de Regras</strong></a>. São ferramentas projetadas para inserir <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-pinned-query"><em>resultados fixados</em></a> nas consultas sem afetar a classificação dos resultados orgânicos com base em regras. Eles apenas adicionam lógica de negócios aos resultados de forma declarativa e simples.</p><p>Alguns casos de uso comuns para regras de consulta são:</p><ul><li><strong>Destacar anúncios ou promoções</strong>: Exibir itens em promoção ou patrocinados no topo.</li><li><strong>Exclusão por contexto ou geolocalização</strong>: Ocultar determinados itens quando as regulamentações locais não permitem que você os mostre.</li><li><strong>Priorizar resultados-chave</strong>: Garantir que as pesquisas populares ou fixas estejam sempre no topo, independentemente do ranking orgânico.</li></ul><p>Para acessar a interface e interagir com essas ferramentas, você precisa clicar no menu lateral do Kibana e ir para <strong>Regras de Consulta</strong>, em <strong>Relevância:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/242e33e89d1a07ffa0e76009c46b3a9236722741-458x1010.png" style="width: 100%;" /><p>Assim que o menu de regras de consulta aparecer, clique em <strong>Criar seu primeiro conjunto de regras:</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/30b3a91bbbf243d314cf38298e01ca5cff784430-1600x945.png" style="width: 100%;" /><p>Em seguida, você precisa dar um nome ao seu conjunto de regras.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/26c5462f88678867776f933b5655ca0df0d72a16-708x446.png" style="width: 100%;" /><p>O formulário para definir cada regra possui três componentes principais:</p><ul><li><strong>Critérios</strong>: As condições que devem ser cumpridas para que a regra se aplique. Por exemplo, “quando o campo query_string contém o valor <em>Christmas</em>” ou “quando o campo country é <em>CO”.</em></li><li><strong>Ação</strong>: Isto é o que você deseja que aconteça quando as condições forem atendidas. Ele pode ser fixado (fixando um documento nos primeiros resultados) ou excluído (ocultando um documento).</li><li><strong>Metadados</strong>: São os campos que acompanham a consulta quando ela é executada. Podem incluir informações do usuário (como localização ou idioma), bem como dados de pesquisa (query_string). Esses são os valores usados pelos critérios para decidir se uma regra deve ou não ser aplicada.</li></ul><h2>Exemplo: itens populares</h2><p>Vamos imaginar que temos um site de comércio eletrônico com diversos itens. Ao analisarmos as métricas, notamos que um dos itens mais vendidos na categoria de consoles é o &quot;Controle sem fio DualShock 4&quot;, especialmente quando os usuários pesquisam pelas palavras-chave &quot;PS4&quot; ou &quot;PlayStation 4&quot;. Assim, decidimos colocar este produto no topo dos resultados, sempre que um usuário pesquisar por essas palavras-chave.</p><p>Primeiro, vamos indexar os documentos de cada item usando uma solicitação de API em lote:</p><pre><code>POST _bulk
{ "index": { "_index": "products", "_id": "1" } }
{ "id": "1", "name": "PlayStation 4 Slim 1TB", "category": "console", "brand": "Sony", "price": 1200 }
{ "index": { "_index": "products", "_id": "2" } }
{ "id": "2", "name": "DualShock 4 Wireless Controller", "category": "accessory", "brand": "Sony", "price": 250 }
{ "index": { "_index": "products", "_id": "3" } }
{ "id": "3", "name": "PlayStation 4 Camera", "category": "accessory", "brand": "Sony", "price": 200 }
{ "index": { "_index": "products", "_id": "4" } }
{ "id": "4", "name": "PlayStation 4 VR Headset", "category": "accessory", "brand": "Sony", "price": 900 }
{ "index": { "_index": "products", "_id": "5" } }
{ "id": "5", "name": "Charging Station for DualShock 4", "category": "accessory", "brand": "Sony", "price": 80 }</code></pre><p>Se não intervirmos na consulta, o item geralmente aparece em quarto lugar. Eis a pergunta:</p><pre><code>GET products/_search
{
 "query": {
   "match": {
     "name": "PlayStation 4"
   }
 }
}</code></pre><p>E aqui estão os resultados.</p><pre><code>{
 "took": 1,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 5,
     "relation": "eq"
   },
   "max_score": 0.6973252,
   "hits": [
     {
       "_index": "products",
       "_id": "3",
       "_score": 0.6973252,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 0.6260078,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 0.6260078,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "2",
       "_score": 0.08701137,
       "_source": {
         "id": "2",
         "name": "DualShock 4 Wireless Controller",
         "category": "accessory",
         "brand": "Sony",
         "price": 250
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.07893815,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><p>Vamos criar uma regra de consulta para alterar isso. Primeiro, vamos adicioná-lo ao conjunto de regras assim:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fdc42646fb3e76a09bca7d19047a76efe343f7a2-1600x650.png" style="width: 100%;" /><p>Ou <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-query-rules-put-ruleset">solicitação de API</a> equivalente:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "rule-1232",
      "type": "pinned",
      "criteria": [
        {
          "type": "exact",
          "metadata": "query_string",
          "values": [
            "PS4",
            "PlayStation 4"
          ]
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "2"
          }
        ]
      }
    }
  ]
}</code></pre><p>Para usar o <strong>conjunto de regras </strong>em nossa consulta, devemos usar um tipo de regra de consulta. Esse tipo de consulta é composto por duas partes principais:</p><pre><code>GET /products/_search
{
 "retriever": {
   "rule": {
     "retriever": {
       "standard": {
         "query": {
           "match": { "name": "PlayStation 4" }
         }
       }
     },
     "match_criteria": {
       "query_string": "PlayStation 4"
     },
     "ruleset_ids": ["my-rules"]
   }
 }
}</code></pre><ul><li><strong>match_criteria</strong>: São os metadados usados para comparar com a consulta do usuário. Neste exemplo, o conjunto de regras é ativado quando o campo query_string tem o valor “PlayStation 4”.</li><li><strong>consulta</strong>: a consulta propriamente dita que será usada para pesquisar e obter os resultados orgânicos.</li></ul><p>Dessa forma, primeiro você executa a consulta orgânica e, em seguida, o Elasticsearch aplica as regras do seu conjunto de regras:</p><pre><code>{
 "took": 17,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 5,
     "relation": "eq"
   },
   "max_score": 1.7014122e+38,
   "hits": [
     {
       "_index": "products",
       "_id": "2",
       "_score": 1.7014122e+38,
       "_source": {
         "id": "2",
         "name": "DualShock 4 Wireless Controller",
         "category": "accessory",
         "brand": "Sony",
         "price": 250
       }
     },
     {
       "_index": "products",
       "_id": "3",
       "_score": 0.6973252,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 0.6260078,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 0.6260078,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.07893815,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><h2>Exemplo: metadados baseados no usuário</h2><p>Outra aplicação interessante das Regras de Consulta é usar metadados para exibir documentos específicos com base em informações contextuais do usuário ou da página da web.</p><p>Por exemplo, vamos supor que queremos destacar itens ou ofertas personalizadas com base no nível de fidelidade do usuário, representado por um valor numérico.</p><p>Podemos fazer isso inserindo esses metadados diretamente na consulta, de forma que as regras sejam ativadas quando o valor atender a determinados critérios.</p><p>Primeiro, vamos indexar um documento que somente usuários com um alto nível de fidelidade podem ver:</p><pre><code>POST _bulk
{ "index": { "_index": "products", "_id": "6" } }
{ "id": "6", "name": "PlayStation Plus Deluxe Card - 12 months", "category": "membership", "brand": "Sony", "price": 300 }</code></pre><p>Agora, vamos criar uma nova regra dentro do mesmo conjunto de regras para que, quando o nível de lealdade for igual ou superior a 80, o item apareça no topo dos resultados.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/58de12dff93305440608f51465462fcc68653a08-1421x496.png" style="width: 100%;" /><p>Salve a regra e o conjunto de regras.</p><p>Aqui está a solicitação REST equivalente:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "pin-premiun-user",
      "type": "pinned",
      "criteria": [
        {
          "type": "gte",
          "metadata": "loyalty_level",
          "values": [
            80
          ]
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "6"
          }
        ]
      }
    }
  ]
}</code></pre><p>Agora, ao executar uma consulta, precisamos incluir o novo parâmetro <strong>loyalty_level </strong>nos metadados. Se a condição da regra for atendida, o novo documento aparecerá no topo dos resultados.</p><p>Por exemplo, ao enviar uma consulta onde o nível de lealdade é 80:</p><pre><code>POST /products/_search
{
  "retriever": {
    "rule": {
      "retriever": {
        "standard": {
          "query": {
            "match": {
              "name": "PlayStation"
            }
          }
        }
      },
      "match_criteria": {
        "query_string": "PlayStation",
        "loyalty_level": 80
      },
      "ruleset_ids": ["my-rules"]
    }
  }
}</code></pre><p>Veremos o documento de fidelidade acima dos resultados:</p><pre><code>{
  "took": 31,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": 1.7014122e+38,
    "hits": [
      {
        "_index": "products",
        "_id": "6",
        "_score": 1.7014122e+38,
        "_source": {
          "id": "6",
          "name": "PlayStation Plus Deluxe Card - 12 months",
          "category": "membership",
          "brand": "Sony",
          "price": 300
        }
      },
      {
        "_index": "products",
        "_id": "3",
        "_score": 0.5054567,
        "_source": {
          "id": "3",
          "name": "PlayStation 4 Camera",
          "category": "accessory",
          "brand": "Sony",
          "price": 200
        }
      },
      {
        "_index": "products",
        "_id": "1",
        "_score": 0.45618832,
        "_source": {
          "id": "1",
          "name": "PlayStation 4 Slim 1TB",
          "category": "console",
          "brand": "Sony",
          "price": 1200
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 0.45618832,
        "_source": {
          "id": "4",
          "name": "PlayStation 4 VR Headset",
          "category": "accessory",
          "brand": "Sony",
          "price": 900
        }
      }
    ]
  }
}</code></pre><p>No caso abaixo, como o nível de fidelidade é 70, a regra não é atendida e o item não deve aparecer no topo:</p><pre><code>POST /products/_search
{
  "retriever": {
    "rule": {
      "retriever": {
        "standard": {
          "query": {
            "match": {
              "name": "PlayStation"
            }
          }
        }
      },
      "match_criteria": {
        "query_string": "PlayStation",
        "loyalty_level": 70
      },
      "ruleset_ids": ["my-rules"]
    }
  }
}</code></pre><p>Aqui estão os resultados:</p><pre><code>{
  "took": 7,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": 0.5054567,
    "hits": [
      {
        "_index": "products",
        "_id": "3",
        "_score": 0.5054567,
        "_source": {
          "id": "3",
          "name": "PlayStation 4 Camera",
          "category": "accessory",
          "brand": "Sony",
          "price": 200
        }
      },
      {
        "_index": "products",
        "_id": "1",
        "_score": 0.45618832,
        "_source": {
          "id": "1",
          "name": "PlayStation 4 Slim 1TB",
          "category": "console",
          "brand": "Sony",
          "price": 1200
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 0.45618832,
        "_source": {
          "id": "4",
          "name": "PlayStation 4 VR Headset",
          "category": "accessory",
          "brand": "Sony",
          "price": 900
        }
      },
      {
        "_index": "products",
        "_id": "6",
        "_score": 0.3817649,
        "_source": {
          "id": "6",
          "name": "PlayStation Plus Deluxe Card - 12 months",
          "category": "membership",
          "brand": "Sony",
          "price": 300
        }
      }
    ]
  }
}</code></pre><h2>Exemplo: exclusão imediata</h2><p>Vamos supor que nosso <strong>Controle Sem Fio DualShock 4 (ID 2)</strong> esteja temporariamente indisponível e não possa ser vendido. Assim, em vez de excluir o documento manualmente ou esperar que algum processamento de dados seja iniciado, a equipe comercial decide removê-lo dos resultados da pesquisa enquanto isso.</p><p>Usaremos um processo semelhante ao que acabamos de aplicar aos itens populares, mas desta vez, em vez de selecionar <em>&quot;Fixados&quot;</em>, escolheremos <em>&quot;Excluir&quot;</em>. Essa regra funciona como uma espécie de lista negra. Altere os critérios para <strong>&quot;Sempre&quot;</strong> para que a exclusão funcione sempre que a consulta for executada.</p><p>A regra deve ser assim:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f10971e4f1bc9520105111adfa3a476581a27130-1600x623.png" style="width: 100%;" /><p>Salve a regra e o conjunto de regras para aplicar as alterações. Aqui está a solicitação REST equivalente:</p><pre><code>PUT _query_rules/my-rules
{
  "rules": [
    {
      "rule_id": "rule-6358",
      "type": "pinned",
      "criteria": [
        {
          "type": "always"
        }
      ],
      "actions": {
        "docs": [
          {
            "_index": "products",
            "_id": "2"
          }
        ]
      }
    }
  ]
}</code></pre><p>Agora, ao executar a consulta novamente, você verá que o item não está mais nos resultados, mesmo que a regra anterior fosse fixá-lo. Isso ocorre porque <strong>as exclusões têm prioridade sobre a fixação dos resultados</strong>.</p><pre><code>{
 "took": 6,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 4,
     "relation": "eq"
   },
   "max_score": 2.205655,
   "hits": [
     {
       "_index": "products",
       "_id": "3",
       "_score": 2.205655,
       "_source": {
         "id": "3",
         "name": "PlayStation 4 Camera",
         "category": "accessory",
         "brand": "Sony",
         "price": 200
       }
     },
     {
       "_index": "products",
       "_id": "1",
       "_score": 1.9738505,
       "_source": {
         "id": "1",
         "name": "PlayStation 4 Slim 1TB",
         "category": "console",
         "brand": "Sony",
         "price": 1200
       }
     },
     {
       "_index": "products",
       "_id": "4",
       "_score": 1.9738505,
       "_source": {
         "id": "4",
         "name": "PlayStation 4 VR Headset",
         "category": "accessory",
         "brand": "Sony",
         "price": 900
       }
     },
     {
       "_index": "products",
       "_id": "5",
       "_score": 0.69247496,
       "_source": {
         "id": "5",
         "name": "Charging Station for DualShock 4",
         "category": "accessory",
         "brand": "Sony",
         "price": 80
       }
     }
   ]
 }
}</code></pre><h2>Conclusão</h2><p><strong>As regras de consulta</strong> tornam muito fácil ajustar a relevância sem qualquer alteração de código. A nova <strong>interface</strong> <strong>do Kibana </strong>permite que você<strong> </strong>Para fazer essas alterações em questão de segundos, você e sua equipe terão mais controle sobre os resultados da pesquisa.</p><p>Além do comércio eletrônico, as Regras de Consulta podem ser aplicadas em muitos outros cenários: destacar guias de solução de problemas em portais de suporte, exibir documentos internos importantes em bases de conhecimento, promover notícias de última hora em sites de notícias ou filtrar anúncios de emprego ou conteúdo expirados. Eles podem até mesmo impor regras de conformidade, como ocultar material restrito por função de usuário ou região.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um agente de conhecimento com recuperação semântica usando Mastra e Elasticsearch.]]></title>
            <description><![CDATA[Aprenda como construir um agente de conhecimento com recuperação semântica usando Mastra e Elasticsearch como armazenamento vetorial para memória e recuperação de informações.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">A Engenharia de Contexto</a> está se tornando cada vez mais importante na construção de agentes e arquiteturas de IA confiáveis. À medida que os modelos se tornam cada vez melhores, sua eficácia e confiabilidade dependem menos dos dados de treinamento e mais de quão bem eles estão fundamentados no contexto correto. Agentes que conseguem recuperar e aplicar as informações mais relevantes no momento certo têm muito mais probabilidade de produzir resultados precisos e confiáveis.</p><p>Neste blog, usaremos <a href="https://mastra.ai/">o Mastra</a> para construir um agente de conhecimento que memoriza o que os usuários dizem e consegue recuperar informações relevantes posteriormente, utilizando o Elasticsearch como backend de memória e recuperação. Você pode facilmente estender esse mesmo conceito a casos de uso do mundo real, como agentes de suporte que conseguem se lembrar de conversas e soluções anteriores, permitindo que eles personalizem as respostas para usuários específicos ou apresentem soluções mais rapidamente com base no contexto prévio.</p><p>Acompanhe aqui como construir isso passo a passo. Se você se perder ou simplesmente quiser executar um exemplo finalizado, confira o repositório <a href="https://github.com/jdarmada/getting-started-mastra-elastic/tree/main">aqui</a>.</p><h2>O que é Mastra?</h2><p>Mastra é um framework TypeScript de código aberto para a construção de agentes de IA com componentes intercambiáveis para raciocínio, memória e ferramentas. Seu recurso <a href="https://mastra.ai/docs/memory/semantic-recall">de recuperação semântica</a> permite que os agentes se lembrem e recuperem interações passadas, armazenando mensagens como representações vetoriais em um banco de dados vetorial. Isso permite que os agentes mantenham o contexto e a continuidade da conversa a longo prazo. O Elasticsearch é um excelente armazenamento de vetores para habilitar esse recurso, pois oferece suporte a buscas vetoriais densas e eficientes. Quando a recuperação semântica é acionada, o agente extrai mensagens relevantes do passado para a janela de contexto do modelo, permitindo que o modelo use esse contexto recuperado como base para seu raciocínio e respostas.</p><h2>O que você precisa para começar</h2><ul><li>Node v18+</li><li>Elasticsearch (versão 8.15 ou mais recente)</li><li>Chave da API do Elasticsearch</li><li><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">Chave da API OpenAI</a></li></ul><p>Observação: você precisará disso porque a demonstração usa o provedor OpenAI, mas o Mastra é compatível com outros SDKs de IA e provedores de modelos da comunidade, então você pode facilmente trocá-lo dependendo da sua configuração.</p><h2>Construindo um projeto Mastra</h2><p>Usaremos a CLI integrada do Mastra para fornecer a estrutura básica do nosso projeto. Execute o comando:</p><pre><code>npm create mastra@latest</code></pre><p>Você receberá uma série de instruções, começando com:</p><p>1. Dê um nome ao seu projeto.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" style="width: 100%;" /><p>2. Podemos manter esta opção padrão; fique à vontade para deixar este campo em branco.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" style="width: 100%;" /><p>3. Para este projeto, usaremos um modelo fornecido pela OpenAI.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" style="width: 100%;" /><p>4. Selecione a opção “Ignorar por enquanto”, pois armazenaremos todas as nossas variáveis de ambiente em um arquivo `.env` que configuraremos em uma etapa posterior.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" style="width: 100%;" /><p>5. Também podemos ignorar esta opção.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" style="width: 100%;" /><p>Assim que a inicialização estiver concluída, podemos passar para a próxima etapa.</p><h3>Instalando dependências</h3><p>Em seguida, precisamos instalar algumas dependências:</p><pre><code>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv</code></pre><ul><li><code>ai</code> - Pacote Core AI SDK que fornece ferramentas para gerenciar modelos de IA, prompts e fluxos de trabalho em JavaScript/TypeScript. O Mastra é construído sobre o <a href="https://ai-sdk.dev/">SDK de IA</a> da Vercel, portanto, precisamos dessa dependência para permitir as interações do modelo com o seu agente.</li><li><code>@ai-sdk/openai</code> - Plugin que conecta o SDK de IA aos modelos da OpenAI (como GPT-4, GPT-4o, etc.), permitindo chamadas à API usando sua chave de API da OpenAI.</li><li><code>@elastic/elasticsearch</code> - <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript">Cliente oficial do Elasticsearch para Node.js</a>, Utilizado para conectar-se ao seu Elastic Cloud ou cluster local para indexação, pesquisa e operações vetoriais.</li><li><code>dotenv</code> - Carrega variáveis de ambiente de um arquivo .env arquivo em process.env, permitindo que você insira credenciais com segurança, como chaves de API e endpoints do Elasticsearch.</li></ul><h3>Configurando variáveis de ambiente</h3><p>Crie um arquivo <code>.env</code> no diretório raiz do seu projeto, caso ainda não exista um. Alternativamente, você pode copiar e renomear o exemplo <code>.env</code> que eu forneci no <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/.env.example">repositório</a>. Neste arquivo, podemos adicionar as seguintes variáveis:</p><pre><code>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"</code></pre><p>Isso conclui a configuração básica. A partir daqui, você já pode começar a construir e orquestrar agentes. Vamos dar um passo além e adicionar o Elasticsearch como camada de armazenamento e busca vetorial.</p><h2>Adicionando o Elasticsearch como armazenamento vetorial.</h2><p>Crie uma nova pasta chamada <code>stores</code> e, dentro dela, adicione este <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/src/mastra/stores/elastic-store.ts">arquivo</a>. Antes que a Mastra e a Elastic lancem uma integração oficial de armazenamento vetorial do Elasticsearch, <a href="https://github.com/abhiaiyer91">Abhi Aiyer</a>(CTO da Mastra) compartilhou esta classe protótipo inicial chamada <code>ElasticVector</code>. Em termos simples, ele conecta a abstração de memória do Mastra aos recursos de vetores densos do Elasticsearch, permitindo que os desenvolvedores utilizem o Elasticsearch como banco de dados de vetores para seus agentes.</p><p>Vamos analisar mais detalhadamente as partes importantes da integração:</p><h3>Ingestão do cliente Elasticsearch</h3><p>Esta seção define a classe <code>ElasticVector</code> e configura a conexão do cliente Elasticsearch com suporte para implantações padrão e sem servidor.</p><pre><code>export interface ElasticVectorConfig extends ClientOptions {
    /**
     * Explicitly specify if connecting to Elasticsearch Serverless.
     * If not provided, will be auto-detected on first use.
     */
    isServerless?: boolean;
    
    /**
     * Maximum documents to count accurately when describing indices.
     * Higher values provide accurate counts but may impact performance on large indices.
     * 
     * @default 10000
     */
    maxCountAccuracy?: number;
}

export class ElasticVector extends MastraVector {
    private client: Client;
    private isServerless: boolean | undefined;
    private deploymentChecked: boolean = false;
    private readonly maxCountAccuracy: number;

    constructor(config: ElasticVectorConfig) {
        super();
        this.client = new Client(config);
        this.isServerless = config.isServerless;
        this.maxCountAccuracy = config.maxCountAccuracy ?? 10000;
    }
}</code></pre><ul><li><code>ElasticVectorConfig extends ClientOptions</code>Isso cria uma nova interface de configuração que herda todas as opções do cliente Elasticsearch (como <code>node</code>, <code>auth</code>, <code>requestTimeout</code>) e adiciona nossas propriedades personalizadas. Isso significa que os usuários podem passar qualquer configuração válida do Elasticsearch juntamente com nossas opções específicas para ambientes sem servidor.</li><li><code>extends MastraVector</code>Isso permite que <code>ElasticVector</code> herde da classe base <code>MastraVector</code> do Mastra, que é uma interface comum à qual todas as integrações de armazenamento vetorial estão em conformidade. Isso garante que o Elasticsearch se comporte como qualquer outro backend vetorial do Mastra da perspectiva do agente.</li><li><code>private client: Client</code>Esta é uma propriedade privada que contém uma instância do cliente JavaScript do Elasticsearch. Isso permite que a classe se comunique diretamente com o seu cluster.</li><li><code>isServerless</code> e <code>deploymentChecked</code>: Essas propriedades funcionam em conjunto para detectar e armazenar em cache se estamos conectados a uma implantação do Elasticsearch sem servidor ou padrão. Essa detecção ocorre automaticamente no primeiro uso ou pode ser configurada explicitamente.</li><li><code>constructor(config: ClientOptions)</code>: Este construtor recebe um objeto de configuração (contendo suas credenciais do Elasticsearch e configurações opcionais sem servidor) e o usa para inicializar o cliente na linha <code>this.client = new Client(config)</code>.</li><li><code>super()</code>Isso chama o construtor base do Mastra, portanto, ele herda o registro de logs, os auxiliares de validação e outros recursos internos.</li></ul><p>Neste ponto, Mastra sabe que existe uma nova loja de vetores chamada <code>ElasticVector</code></p><h3>Detecção do tipo de implantação</h3><p>Antes de criar os índices, o adaptador detecta automaticamente se você está usando o Elasticsearch padrão ou o Elasticsearch Serverless. Isso é importante porque as implantações sem servidor não permitem a configuração manual de shards.</p><pre><code>private async detectServerless(): Promise<boolean> {
    // Return cached result if already detected
    if (this.deploymentChecked) {
        return this.isServerless ?? false;
    }

    // Use explicit configuration if provided
    if (this.isServerless !== undefined) {
        this.deploymentChecked = true;
        this.logger?.info(
            `Using explicit deployment type: ${this.isServerless ? 'Serverless' : 'Standard'}`
        );
        return this.isServerless;
    }

    try {
        const info = await this.client.info();
        
        // Primary detection: build flavor (most reliable)
        const isBuildFlavorServerless = info.version?.build_flavor === 'serverless';
        
        // Secondary detection: tagline (fallback)
        const isTaglineServerless = info.tagline?.toLowerCase().includes('serverless') ?? false;
        
        this.isServerless = isBuildFlavorServerless || isTaglineServerless;
        this.deploymentChecked = true;
        
        this.logger?.info(
            `Auto-detected ${this.isServerless ? 'Serverless' : 'Standard'} Elasticsearch deployment`,
            { 
                buildFlavor: info.version?.build_flavor, 
                version: info.version?.number,
                detectionMethod: isBuildFlavorServerless ? 'build_flavor' : 'tagline'
            }
        );
        
        return this.isServerless;
    } catch (error) {
        this.logger?.warn(
            'Could not auto-detect deployment type, assuming Standard Elasticsearch. ' +
            'Set isServerless: true explicitly in config if using Serverless.',
            { error: error instanceof Error ? error.message : String(error) }
        );
        this.isServerless = false;
        this.deploymentChecked = true;
        return false;
    }
}</code></pre><p>O que está acontecendo:</p><ul><li>Primeiro verifica se você definiu explicitamente <code>isServerless</code> na configuração (ignora a detecção automática).</li><li>Chama a API <code>info()</code> do Elasticsearch para obter informações do cluster.</li><li>Verifica o <code>build_flavor field</code> (implantações sem servidor retornam <code>serverless</code>)</li><li>Se a opção de build não estiver disponível, a solução é verificar a descrição da versão.</li><li>Armazena o resultado em cache para evitar chamadas repetidas à API.</li><li>Se a detecção falhar, a implantação padrão será utilizada por padrão.</li></ul><p> Exemplo de uso:</p><pre><code>// Option 1: Auto-detect (recommended)
const vector = new ElasticVector({
    node: 'https://your-cluster.es.cloud',
    auth: { apiKey: 'your-api-key' }
});
// Detection happens automatically on first index operation

// Option 2: Explicit configuration (faster startup)
const vector = new ElasticVector({
    node: 'https://your-serverless.es.cloud',
    auth: { apiKey: 'your-api-key' },
    isServerless: true  // Skips auto-detection
});</code></pre><h3>Criando o armazenamento de “memória” no Elasticsearch</h3><p>A função abaixo configura um índice Elasticsearch para armazenar embeddings. Verifica se o índice já existe. Caso contrário, cria um com o mapeamento abaixo que contém um campo <code>dense_vector</code> para armazenar embeddings e métricas de similaridade personalizadas.</p><p>Algumas coisas a ter em conta:</p><ul><li>O parâmetro <code>dimension</code> representa o comprimento de cada vetor de incorporação, que depende do modelo de incorporação que você está usando. Em nosso caso, geraremos embeddings usando o modelo <code>text-embedding-3-small</code> da OpenAI, que produz vetores de tamanho <code>1536</code>. Usaremos esse valor como padrão.</li><li>A variável <code>similarity</code> usada no mapeamento abaixo é definida pela função auxiliar c<code>onst similarity = this.mapMetricToSimilarity(metric)</code>, que recebe o valor do parâmetro <code>metric</code> e o converte em uma palavra-chave compatível com o Elasticsearch para a métrica de distância escolhida.<ul><li>Por exemplo: Mastra usa termos gerais para similaridade de vetores como <code>cosine</code>, <code>euclidean</code> e <code>dotproduct</code>. Se passássemos a métrica <code>euclidean</code> diretamente para o mapeamento do Elasticsearch, ele geraria um erro porque o Elasticsearch espera que a palavra-chave <code>l2_norm</code> represente a distância euclidiana.</li></ul></li><li>Compatibilidade com ambientes sem servidor: o código omite automaticamente as configurações de shard e réplica para implantações sem servidor, pois estas são gerenciadas automaticamente pelo Elasticsearch Serverless.</li></ul><pre><code>async createIndex(params: CreateIndexParams): Promise<void> {
    const { indexName, dimension = 1536, metric = 'cosine' } = params;

    try {
        const exists = await this.client.indices.exists({ index: indexName });

        if (exists) {
            try {
                await this.validateExistingIndex(indexName, dimension, metric);
                this.logger?.info(`Index "${indexName}" already exists and is valid`);
                return;
            } catch (validationError) {
                throw new Error(
                    `Index "${indexName}" exists but does not match the required configuration: ${
                        validationError instanceof Error ? validationError.message : String(validationError)
                    }`
                );
            }
        }

        const isServerless = await this.detectServerless();
        const similarity = this.mapMetricToSimilarity(metric);

        const indexConfig: any = {
            index: indexName,
            mappings: {
                properties: {
                    vector: {
                        type: 'dense_vector',
                        dims: dimension,
                        index: true,
                        similarity: similarity,
                    },
                    metadata: {
                        type: 'object',
                        enabled: true,
                        dynamic: true, // Allows flexible metadata structures
                    },
                },
            },
        };

        // Only configure shards/replicas for non-serverless deployments
        // Serverless manages infrastructure automatically
        if (!isServerless) {
            indexConfig.settings = {
                number_of_shards: 1,
                number_of_replicas: 0, // Increase for production HA deployments
            };
        }

        await this.client.indices.create(indexConfig);

        this.logger?.info(
            `Created ${isServerless ? 'Serverless' : 'Standard'} Elasticsearch index "${indexName}"`,
            { dimension, metric, similarity }
        );
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to create index "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to create index "${indexName}": ${errorMessage}`);
    }
}</code></pre><h3>Armazenar uma nova memória ou anotação após uma interação.</h3><p>Esta função recebe novos embeddings gerados após cada interação, juntamente com os metadados, e os insere ou atualiza no índice usando a API <code>bulk</code> do Elastic. A API <code>bulk</code> agrupa várias operações de gravação em uma única solicitação; essa melhoria no desempenho de indexação garante que as atualizações permaneçam eficientes à medida que a memória do nosso agente continua a crescer.</p><pre><code>async upsert(params: UpsertVectorParams): Promise<string[]> {
    const { indexName, vectors, metadata = [], ids } = params;

    try {
        // Generate unique IDs if not provided
        const vectorIds = ids || vectors.map((_, i) => 
            `vec_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 9)}`
        );

        const operations = vectors.flatMap((vec, index) => [
            { index: { _index: indexName, _id: vectorIds[index] } },
            {
                vector: vec,
                metadata: metadata[index] || {},
            },
        ]);

        const response = await this.client.bulk({
            refresh: true,
            operations,
        });

        if (response.errors) {
            const erroredItems = response.items.filter((item: any) => item.index?.error);
            const erroredIds = erroredItems.map((item: any) => item.index?._id);
            const errorDetails = erroredItems.slice(0, 3).map((item: any) => ({
                id: item.index?._id,
                error: item.index?.error?.reason || item.index?.error,
                type: item.index?.error?.type
            }));
            
            const errorMessage = `Failed to upsert ${erroredIds.length}/${vectors.length} vectors`;
            console.error(`${errorMessage}. Sample errors:`, JSON.stringify(errorDetails, null, 2));
            this.logger?.error(errorMessage, { 
                failedCount: erroredIds.length, 
                totalCount: vectors.length,
                sampleErrors: errorDetails 
            });
            
            // Still return successfully inserted IDs
            const successfulIds = vectorIds.filter((id, idx) => 
                !erroredIds.includes(id)
            );
            
            if (successfulIds.length === 0) {
                throw new Error(`${errorMessage}. All operations failed. See logs for details.`);
            }
            
            return successfulIds;
        }

        this.logger?.info(`Successfully upserted ${vectors.length} vectors to "${indexName}"`);
        return vectorIds;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
    }
}</code></pre><h3>Consultar vetores semelhantes para recuperação semântica</h3><p>Essa função é o núcleo do recurso de recuperação semântica. O agente utiliza a busca vetorial para encontrar incorporações armazenadas semelhantes em nosso índice.</p><pre><code>async query(params: QueryVectorParams<any>): Promise<QueryResult[]> {
    const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;

    try {
        const knnQuery: any = {
            field: 'vector',
            query_vector: queryVector,
            k: topK,
            num_candidates: Math.max(topK * 10, 100), // Search more candidates for better recall
        };

        // Apply metadata filters if provided
        if (filter) {
            knnQuery.filter = this.buildElasticFilter(filter);
        }

        const sourceFields = ['metadata'];
        if (includeVector) {
            sourceFields.push('vector');
        }

        const response = await this.client.search({
            index: indexName,
            knn: knnQuery,
            size: topK,
            _source: sourceFields,
        });

        const results = response.hits.hits.map((hit: any) => ({
            id: hit._id,
            score: hit._score || 0,
            metadata: hit._source?.metadata || {},
            vector: includeVector ? hit._source?.vector : undefined,
        }));

        this.logger?.debug(`Query returned ${results.length} results from "${indexName}"`);
        return results;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
    }
}</code></pre><p>Por dentro do capô:</p><ul><li>Executa uma consulta <a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN</a> (k-vizinhos mais próximos) usando a API <code>knn</code> no Elasticsearch.</li><li>Recupera os K vetores mais semelhantes ao vetor de consulta de entrada.</li><li>Opcionalmente, aplica filtros de metadados para refinar os resultados (por exemplo, pesquisar apenas dentro de uma categoria ou intervalo de tempo específico).</li><li>Retorna resultados estruturados, incluindo o ID do documento, a pontuação de similaridade e os metadados armazenados.</li></ul><h2>Criando o agente de conhecimento</h2><p>Agora que vimos a conexão entre Mastra e Elasticsearch por meio da integração <code>ElasticVector</code> , vamos criar o próprio Agente de Conhecimento.</p><p>Dentro da pasta <code>agents</code>, crie um arquivo chamado <code>knowledge-agent.ts</code>. Podemos começar conectando nossas variáveis de ambiente e inicializando o cliente Elasticsearch.</p><pre><code>import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { openai } from '@ai-sdk/openai';
import { Client } from '@elastic/elasticsearch';
import { ElasticVector } from '../stores/elastic-store';
import dotenv from "dotenv";

dotenv.config();

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;

//Error check for undefined credentials
if (!ELASTICSEARCH_ENDPOINT || !ELASTICSEARCH_API_KEY) {
  throw new Error('Missing Elasticsearch credentials');
}

//Check to see if a connection can be established
const testClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { 
    apiKey: ELASTICSEARCH_API_KEY 
  },
});

try {
  await testClient.ping();
  console.log('Connected to Elasticsearch successfully');
} catch (error: unknown) {
  if (error instanceof Error) {
    console.error('Failed to connect to Elasticsearch:', error.message);
  } else {
    console.error('Failed to connect to Elasticsearch:', error);
  }
  process.exit(1);
}
//Initialize the Elasticsearch vector store
const vectorStore = new ElasticVector({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
//Optional: Explicitly set to true if using Elasticsearch Serverless to skip auto-detection and improve startup time
//isServerless: true,
});</code></pre><p>Aqui, nós:</p><ul><li>Use <code>dotenv</code> para carregar nossas variáveis do nosso arquivo <code>.env</code> .</li><li>Verifique se as credenciais do Elasticsearch estão sendo inseridas corretamente e, em seguida, poderemos estabelecer uma conexão bem-sucedida com o cliente.</li><li>Passe o endpoint do Elasticsearch e a chave da API para o construtor <code>ElasticVector</code> para criar uma instância do nosso armazenamento de vetores que definimos anteriormente.</li><li>Opcionalmente, especifique <code>isServerless: true</code> se estiver usando o Elasticsearch Serverless. Isso elimina a etapa de detecção automática e melhora o tempo de inicialização. Caso seja omitido, o adaptador detectará automaticamente o seu tipo de implantação na primeira utilização.</li></ul><p>Em seguida, podemos definir o agente usando a classe <code>Agent</code> do Mastra.</p><pre><code>export const knowledgeAgent = new Agent({
    name: 'KnowledgeAgent',
    instructions: 'You are a helpful knowledge assistant.',
    model: openai('gpt-4o'),
    memory: new Memory({

        vector: vectorStore,

        //embedder used to create embeddings for each message
        embedder: 'openai/text-embedding-3-small',

        //set semantic recall options
        options: {
            semanticRecall: {
                topK: 3, // retrieve 3 similar messages
                messageRange: 2, // include 2 messages before/after each match
                scope: 'resource',
            },
        },
    }),
});</code></pre><p>Os campos que podemos definir são:</p><ul><li><code>name</code> e <code>instructions</code>: Dê a ele uma identidade e uma função primária.</li><li><code>model</code>Estamos usando o <code>gpt-4o</code> da OpenAI por meio do pacote <code>@ai-sdk/openai</code> .</li><li><code>memory</code>:<ul><li><code>vector</code>: Aponta para o nosso armazenamento Elasticsearch, de onde os embeddings são armazenados e recuperados.</li><li><code>embedder</code>Qual modelo usar para gerar embeddings?</li><li><code>semanticRecall</code> As opções definem como funciona o recall:<ul><li><code>topK</code>Quantas mensagens semanticamente semelhantes devem ser recuperadas?</li><li><code>messageRange</code>: Qual a extensão da conversa a ser incluída em cada interação?</li><li><code>scope</code>Define o limite da memória.</li></ul></li></ul></li></ul><p>Quase pronto. Basta adicionarmos esse agente recém-criado à nossa configuração do Mastra. No arquivo chamado <a href="http://index.ts/"><code>index.ts</code></a>, importe o agente de conhecimento e insira-o no campo <code>agents</code> .</p><pre><code>export const mastra = new Mastra({
  agents: { knowledgeAgent },
  storage: new LibSQLStore({
    // stores observability, scores, ... into memory storage, if it needs to persist, change to file:../mastra.db
    url: ":memory:",
  }),
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
  telemetry: {
    // Telemetry is deprecated and will be removed in the Nov 4th release
    enabled: false, 
  },
  observability: {
    // Enables DefaultExporter and CloudExporter for AI tracing
    default: { enabled: true }, 
  },
});</code></pre><p>Os outros campos incluem:</p><ul><li><code>storage</code>Este é o repositório de dados interno do Mastra para histórico de execuções, métricas de observabilidade, pontuações e caches. Para obter mais informações sobre o sistema de armazenamento Mastra, visite <a href="https://mastra.ai/docs/server-db/storage">aqui</a>.</li><li><code>logger</code>O Mastra utiliza <a href="https://github.com/pinojs/pino">o Pino</a>, que é um registrador JSON estruturado e leve. Ele registra eventos como início e término de agentes, chamadas e resultados de ferramentas, erros e tempos de resposta do LLM.</li><li><code>observability</code>Controla o rastreamento de IA e a visibilidade da execução de agentes. Ele rastreia:<ul><li>Início/fim de cada etapa de raciocínio.</li><li>Qual modelo ou ferramenta foi utilizada?</li><li>Entradas e saídas.</li><li>Pontuações e avaliações</li></ul></li></ul><h3>Testando o agente com o Mastra Studio</h3><p>Parabéns! Se você chegou até aqui, está pronto para executar este agente e testar suas capacidades de recuperação semântica. Felizmente, o Mastra oferece uma interface de chat integrada, então não precisamos criar a nossa própria.</p><p>Para iniciar o servidor de desenvolvimento do Mastra, abra um terminal e execute o seguinte comando:</p><pre><code>npm run dev</code></pre><p>Após a inicialização e o empacotamento iniciais do servidor, você deverá receber um endereço para o Playground.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" style="width: 100%;" /><p>Cole este endereço no seu navegador e você será direcionado para o Mastra Studio.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" style="width: 100%;" /><p>Selecione a opção <code>knowledgeAgent</code> e comece a conversar.</p><p>Para um teste rápido para verificar se tudo está conectado corretamente, forneça algumas informações como: &quot;A equipe anunciou que o desempenho de vendas em outubro aumentou 12%, impulsionado principalmente por renovações de contratos corporativos.&quot; O próximo passo é expandir o alcance aos clientes de médio porte.” Em seguida, inicie um novo bate-papo e faça uma pergunta como: &quot;Em qual segmento de clientes dissemos que precisamos nos concentrar a seguir?&quot; O agente de conhecimento deve ser capaz de recordar as informações que você lhe forneceu na primeira conversa. Você deverá ver uma resposta semelhante a esta:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" style="width: 100%;" /><p>Ao recebermos uma resposta como essa, significa que o agente armazenou com sucesso nossa mensagem anterior como embeddings no Elasticsearch e a recuperou posteriormente usando a busca vetorial.</p><h3>Inspecionando o armazenamento de memória de longo prazo do agente.</h3><p>Acesse a aba <code>memory</code> na configuração do seu agente no Mastra Studio. Isso permite que você veja o que seu agente aprendeu ao longo do tempo. Cada mensagem, resposta e interação que é incorporada e armazenada no Elasticsearch passa a fazer parte dessa memória de longo prazo. Você pode realizar buscas semânticas em interações passadas para encontrar rapidamente informações ou contextos que o agente aprendeu anteriormente. Este é essencialmente o mesmo mecanismo que o agente usa durante a recuperação semântica, mas aqui você pode inspecioná-lo diretamente. No exemplo abaixo, estamos pesquisando o termo &quot;vendas&quot; e obtendo como resultado todas as interações que incluíram algo relacionado a vendas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" style="width: 100%;" /><h2>Conclusão</h2><p>Ao conectar o Mastra e o Elasticsearch, podemos fornecer memória aos nossos agentes, o que é uma camada fundamental na engenharia de contexto. Com a recuperação semântica, os agentes podem construir contexto ao longo do tempo, fundamentando suas respostas no que aprenderam. Isso significa interações mais precisas, confiáveis e naturais.</p><p>Essa integração inicial é apenas o ponto de partida. O mesmo padrão pode permitir que agentes de suporte se lembrem de chamados anteriores, bots internos recuperem documentação relevante ou assistentes de IA consigam recordar detalhes do cliente no meio da conversa. Também estamos trabalhando para uma integração oficial com o Mastra, tornando essa combinação ainda mais perfeita em um futuro próximo.</p><p>Estamos ansiosos para ver o que você vai construir em seguida. Experimente, explore <a href="https://mastra.ai/">o Mastra</a> e seus recursos de memória e sinta-se à vontade para compartilhar suas descobertas com a comunidade.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/multimodal-search-siglip-2-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/multimodal-search-siglip-2-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
            <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Você já quis pesquisar seu álbum de fotos por significado? Experimente buscas como &quot;mostre-me fotos minhas onde estou usando uma jaqueta azul e sentado em um banco&quot;, &quot;mostre-me fotos do Monte Everest&quot; ou &quot;saquê e sushi&quot;. 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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" style="width: 100%;" /><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.<br/>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>Encontre fotos de um pico de montanha pelo nome.</li><li>Adivinhe o nome do pico da montanha a partir de uma imagem e encontre picos semelhantes no álbum de fotos.</li><li>Fazer com que as consultas de conceito funcionem (<em>pessoa</em>, <em>rio</em>, <em>bandeiras de oração</em>, <em>etc.)</em></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" style="width: 100%;" /><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 &quot;encontrar os vizinhos mais próximos&quot;.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" style="width: 100%;" /><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Campo</td><td style="border: 1px solid #ccc; padding: 4px;">Tipo</td><td style="border: 1px solid #ccc; padding: 4px;">Exemplo</td><td style="border: 1px solid #ccc; padding: 4px;">Objetivo/Observações</td><td style="border: 1px solid #ccc; padding: 4px;">Vetor/Indexação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">eu ia</td><td style="border: 1px solid #ccc; padding: 4px;">palavra-chave</td><td style="border: 1px solid #ccc; padding: 4px;">ama-dablam</td><td style="border: 1px solid #ccc; padding: 4px;">Slug/ID estável</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">nomes</td><td style="border: 1px solid #ccc; padding: 4px;">texto + subcampo de palavra-chave</td><td style="border: 1px solid #ccc; padding: 4px;">["Ama Dablam","Amadablam"]</td><td style="border: 1px solid #ccc; padding: 4px;">Aliases / nomes multilíngues; names.raw para filtros exatos</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">latlon</td><td style="border: 1px solid #ccc; padding: 4px;">ponto_geográfico</td><td style="border: 1px solid #ccc; padding: 4px;">{"lat":27.8617,"lon":86.8614}</td><td style="border: 1px solid #ccc; padding: 4px;">Coordenadas GPS do pico como uma combinação de latitude/longitude (opcional)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">elev_m</td><td style="border: 1px solid #ccc; padding: 4px;">inteiro</td><td style="border: 1px solid #ccc; padding: 4px;">6812</td><td style="border: 1px solid #ccc; padding: 4px;">Elevação (opcional)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">texto incorporado</td><td style="border: 1px solid #ccc; padding: 4px;">dense_vector</td><td style="border: 1px solid #ccc; padding: 4px;">768</td><td style="border: 1px solid #ccc; padding: 4px;">Protótipo misto (com instruções e, opcionalmente, 1 a 3 imagens de referência) para este pico.</td><td style="border: 1px solid #ccc; padding: 4px;">índice:true, similaridade:"cosseno", opções_de_índice:{type:"hnsw", m:16, ef_construction:128}</td>
                    </tr>
              </tbody>
            </table>
          <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 &quot;Que montanha é esta?&quot; 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>Crie um protótipo de texto usando:<ul><li>nomes dos picos</li><li><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:<ul><li>“uma foto natural do pico da montanha {name} no Himalaia, Nepal”</li><li>“{name} pico emblemático na região de Khumbu, paisagem alpina”</li><li>“{name} cume da montanha, neve, crista rochosa”</li></ul></li><li><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 &quot;pintura, ilustração, pôster, mapa, logotipo&quot; para que haja uma preferência por fotos reais.</li></ul></li><li>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.</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><pre><code>def l2norm(v: np.ndarray) -> 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,
) -> 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 (<= 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 > 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]</code></pre><p>Documento de exemplo do índice <code>peaks_catalog</code> :</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bc05fbd0c4f8d721d5170c28a3884a9eda80bb7d-1210x1132.png" style="width: 100%;" /><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>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.</li><li>Informações de GPS e horário da imagem.</li><li>Vetor denso para codificação de imagem gerado por SigLIP-2.</li><li><code>predicted_peaks</code> Isso nos permite filtrar pelo nome do pico.<br/><br/><strong>Mapeamento de índice</strong></li></ul>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Campo</td><td style="border: 1px solid #ccc; padding: 4px;">Tipo</td><td style="border: 1px solid #ccc; padding: 4px;">Exemplo</td><td style="border: 1px solid #ccc; padding: 4px;">Objetivo/Observações</td><td style="border: 1px solid #ccc; padding: 4px;">Vetor / Indexação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">caminho</td><td style="border: 1px solid #ccc; padding: 4px;">palavra-chave</td><td style="border: 1px solid #ccc; padding: 4px;">dados/imagens/IMG_1234.HEIC</td><td style="border: 1px solid #ccc; padding: 4px;">Como a interface do usuário abre a miniatura/imagem completa</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">imagem_recortada</td><td style="border: 1px solid #ccc; padding: 4px;">dense_vector</td><td style="border: 1px solid #ccc; padding: 4px;">768</td><td style="border: 1px solid #ccc; padding: 4px;">Incorporação de imagem SigLIP-2</td><td style="border: 1px solid #ccc; padding: 4px;">índice:true, similaridade:"cosseno", opções_de_índice:{type:"hnsw", m:16, ef_construction:128}</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">picos_previstos</td><td style="border: 1px solid #ccc; padding: 4px;">palavra-chave</td><td style="border: 1px solid #ccc; padding: 4px;">["ama-dablam","pumori"]</td><td style="border: 1px solid #ccc; padding: 4px;">Top-K palpites no momento da indexação (filtro/faceta de UX barato)</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">GPS</td><td style="border: 1px solid #ccc; padding: 4px;">ponto_geográfico</td><td style="border: 1px solid #ccc; padding: 4px;">{"lat":27.96,"lon":86.83}</td><td style="border: 1px solid #ccc; padding: 4px;">Ativa filtros geográficos</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">tempo_de_tiro</td><td style="border: 1px solid #ccc; padding: 4px;">date</td><td style="border: 1px solid #ccc; padding: 4px;">2023-10-18T09:41:00Z</td><td style="border: 1px solid #ccc; padding: 4px;">Tempo de captura: classificar/filtrar</td><td style="border: 1px solid #ccc; padding: 4px;">—</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Estratégia de indexação para o índice de fotos: </strong>Para cada foto no álbum, fazemos o seguinte:<br/> 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><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> .</li><li><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.</li><li>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.</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><pre><code>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",
) -> 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) >= 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")</code></pre><p>Exemplo de documento do índice de fotos:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1dc1406ac2a97440b6804838795b3c2205c4c6b2-1080x1234.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c75d0ed0ce53d28a94bf2f47a354e25581d2baf-1600x1402.png" style="width: 100%;" /><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 &quot;bandeiras de oração&quot;) 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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b5fac28ffdbedb820505365ca07df125cd01b939-946x370.png" style="width: 100%;" /><p><strong>Consulta Elasticsearch com filtro geográfico:</strong></p><pre><code>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"
       }
     }
}</code></pre><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a3d5635bee7a222b95529dd7f9fbee016381610-1226x550.png" style="width: 100%;" /><p><strong>Consulta do Elasticsearch:</strong></p><p>Passo 1: Encontre os nomes de pico correspondentes.</p><pre><code>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...
         ]
       }
     }
   ]
 }
}</code></pre><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><pre><code>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"
       }
     }
}</code></pre><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>&quot;Ver imagem&quot;</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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" style="width: 100%;" /><p><strong>Pesquisa por texto</strong>: Encontre os picos correspondentes no álbum a partir do texto.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" style="width: 100%;" /><h2>Conclusão</h2><p>Tudo começou com <em>uma pergunta: &quot;Podemos ver as  fotos<strong>do Ama Dablam ?&quot;</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><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>.</li><li><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.</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><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></li><li><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></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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" style="width: 100%;" />]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/multilingual-embedding-model-hybrid-search-reranking</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/multilingual-embedding-model-hybrid-search-reranking</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
            <pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d341b04acecc8eeec321f5404e1643447ecc8526-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><pre><code># 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": {...}
       }
     },
     . . .</code></pre><h2>Realizar uma pesquisa em inglês</h2><p>Vamos tentar realizar a pesquisa em inglês e ver como funciona:</p><pre><code>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"
    }
  }
}
}</code></pre><pre><code>{
       "_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"
       }
     },
     . . .</code></pre><p>Aqui, embora a consulta pareça enganosamente simples, estamos buscando, nos bastidores, as representações numéricas da palavra &#x27;kitty&#x27; 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 &#x27;gatinho&#x27;: “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><pre><code>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"
     }
   }
 }
}</code></pre><pre><code>{
 "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"
}
. . .</code></pre><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><pre><code>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: 고양이"
     }
   }
 }
}</code></pre><pre><code> {
       {
         "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"
       }
     },</code></pre><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 &quot;qual a cor do gato?&quot;, retornará muitos resultados, mas os dois primeiros podem não ser os mais relevantes.</p><pre><code>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ì?"
     }
   }
 }
}</code></pre><p>Todos os resultados mencionam gato ou alguma forma de cor:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/33e1e887dbbdd1066cfedc7375f5e3b46538529e-859x847.png" style="width: 100%;" /><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><pre><code>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ì?"
  }
}
}</code></pre><pre><code> {
       "_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"
       }
     },</code></pre><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>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/multilingual-embedding-model-deployment-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/multilingual-embedding-model-deployment-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Quynh Nguyen]]></dc:creator>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f2aa7bebb6b2f701e274ba7282273f9ab4abed6-720x432.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><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.</li><li><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.</li><li><strong>Relevância</strong>: Oferece resultados mais relevantes ao focar na similaridade conceitual entre consultas e documentos.</li></ul><p>Por exemplo, considere um pesquisador acadêmico que estuda o &quot;impacto das mídias sociais no discurso político&quot; em diferentes países. Com a pesquisa vetorial, eles podem inserir consultas como &quot;l&#x27;impatto dei social media sul discorso politico&quot; (italiano) ou &quot;ảnh hưởng của mạng xã hội đối với diễn ngôn chính trị&quot; (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>Consultas e termos de pesquisa em outros idiomas em um conjunto de dados em inglês, e</li><li>Consultas em vários idiomas sobre um conjunto de dados que contém documentos em vários idiomas.</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>Python 3.6+</li><li>Elasticsearch 8+</li><li>Cliente Python do Elasticsearch: pip install elasticsearch</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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d4f0632529c71a22fbdecf21c9f4f0bb64b8e69c-1600x567.png" style="width: 100%;" /><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><pre><code>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&config=default&split=restval&offset=0&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)</code></pre><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><pre><code>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)</code></pre><p>c) Dados de índice</p><pre><code># 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!")</code></pre><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 &quot;Implantar&quot;, 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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/95cd8f352425d1db2d04b00c3c88d1e71d1ef19a-1600x440.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1599d99949dda8267acc58f400a403a3af5373ef-1600x655.png" style="width: 100%;" /><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><pre><code>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</code></pre><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><strong> </strong>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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cfe189e13dc118d325e7fb90bdace0c912e29f51-1088x1600.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1e043c1c3279b66fbdf19c06b41e76e613043998-1600x1126.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8804d31b4f869078e3b2245040bbb0ab1720a94a-1600x1084.png" style="width: 100%;" /><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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e34b1b18f5b24420d4581fe4d657c569926c2023-1600x1126.png" style="width: 100%;" /><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><pre><code>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"
   }
 }
]
}</code></pre><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><pre><code>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" 
       }
     }
   }
 }
}</code></pre><p>Agora, podemos reindexar os documentos originais para um novo índice, com nosso pipeline de ingestão que irá &quot;vetorizar&quot; ou criar embeddings para o campo de descrição.</p><pre><code>POST _reindex?wait_for_completion=false
{
 "source": {
   "index": "coco"
 },
 "dest": {
   "index": "coco_multilingual",
   "pipeline": "vectorize_descriptions"
 }
}</code></pre><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>
        </item>
        <item>
            <title><![CDATA[Servidor Elastic MCP: Exponha as ferramentas do Agent Builder a qualquer agente de IA.]]></title>
            <description><![CDATA[Descubra como usar o servidor Elastic MCP integrado no Agent Builder para estender com segurança qualquer agente de IA com acesso aos seus dados privados e ferramentas personalizadas.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elastic-mcp-server-agent-builder-tools</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elastic-mcp-server-agent-builder-tools</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jedr Blaszyk, Joe McElroy]]></dc:creator>
            <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Elastic Agent Builder é uma plataforma para criar ferramentas e agentes que se integram profundamente aos seus próprios dados no Elasticsearch. Por exemplo, você pode criar ferramentas que realizam buscas semânticas em documentos internos, analisam logs de observabilidade ou consultam alertas de segurança.</p><p>Mas a verdadeira mágica acontece quando você consegue integrar essas ferramentas personalizadas e orientadas a dados aos ambientes onde você passa a maior parte do tempo. E se o agente do seu editor de código pudesse acessar com segurança a base de conhecimento privada da sua organização?</p><p>É aí que entra <strong>o Protocolo de Contexto do Modelo (MCP)</strong> . O Elastic Agent Builder é fornecido com um servidor MCP integrado que dá acesso às ferramentas da plataforma.</p><h2>Por que usar o servidor Elastic Agent Builder MCP?</h2><p>Os agentes de IA são incrivelmente poderosos, mas seu conhecimento geralmente se limita aos dados com os quais foram treinados e às informações que podem pesquisar ativamente na internet pública. Eles não conhecem os documentos de design internos da sua empresa, os manuais de implantação específicos da sua equipe ou a estrutura exclusiva dos logs de seus aplicativos.</p><p>O desafio é fornecer ao seu assistente de IA o contexto especializado de que ele precisa. Este é precisamente o problema que o MCP foi projetado para resolver. <strong>MCP é um padrão aberto que permite que um modelo ou agente de IA descubra e utilize ferramentas externas.</strong></p><p>Para tornar isso possível, o Elastic Agent Builder expõe nativamente suas ferramentas personalizadas por meio de um servidor MCP integrado. Isso significa que você pode conectar facilmente qualquer cliente compatível com MCP, como <strong>Cursor</strong>, <strong>VS Code</strong> ou <strong>Claude Desktop</strong>, com as ferramentas especializadas e com reconhecimento de dados que você criou com o Elastic Agent Builder.</p><h2>Quando usar MCP (e quando não usar)</h2><p>O Elastic Agent Builder inclui diversos protocolos para suportar diferentes padrões de integração. Escolher a opção certa é fundamental para criar fluxos de trabalho de IA eficazes.</p><ul><li><strong>Use <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">o MCP</a></strong> para aprimorar seu agente de IA (como no <strong>Cursor</strong> ou <strong>no VS Code</strong>) com ferramentas especializadas. Trata-se da abordagem &quot;traga suas próprias ferramentas&quot;, que aprimora o assistente que você já usa com acesso seguro aos seus dados privados. Somente as ferramentas são expostas através do servidor MCP — os agentes da Elastic são independentes disso.</li><li><strong>Utilize o <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">protocolo A2A</a></strong> para permitir que seu Elastic Agent totalmente personalizado colabore com outros agentes autônomos (como no <a href="https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise"><strong>Gemini Enterprise do Google</strong></a>). Isso se aplica à delegação entre agentes, onde cada agente trabalha em conjunto para resolver um problema.</li><li><strong>Utilize <a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api">as APIs do Agent Builder</a></strong> para obter controle programático completo ao criar um aplicativo personalizado do zero.</li></ul><p>Para um desenvolvedor que busca respostas em sua documentação interna sem sair do seu IDE, o MCP é a solução ideal.</p><h2>Exemplo: suas ferramentas personalizadas no Cursor com o servidor Agent Builder MCP</h2><p>Vamos analisar um exemplo prático que eu uso diariamente. Primeiro, rastreei e indexei nossa documentação interna de engenharia em um índice Elasticsearch chamado <code>elastic-dev-docs</code>. Embora pudéssemos usar as ferramentas genéricas e integradas disponíveis no Agent Builder, criaremos nossa própria ferramenta personalizada para consultar essa base de conhecimento específica.</p><p>O motivo para construir uma ferramenta personalizada é simples: <strong>controle e precisão</strong>. Essa abordagem nos dá o poder de executar uma consulta semântica rápida diretamente em nosso índice <code>elastic-dev-docs</code> . Temos controle total sobre qual índice é o alvo e como os dados são obtidos.</p><p>Agora, veja como podemos usar essa base de conhecimento personalizada em um editor de código com inteligência artificial, como o Cursor.</p><h3>Etapa 1: Crie uma ferramenta de base de conhecimento personalizada no Agent Builder.</h3><p>Primeiro, crie uma nova ferramenta no Construtor de Agentes. Uma descrição clara e específica da ferramenta é importante porque é assim que qualquer agente de IA, seja o Elastic Agent interno ou uma ferramenta externa como o Cursor, conectado via MCP, descobre e seleciona a ferramenta adequada para a tarefa correta.</p><p>Uma descrição precisa deve ser explícita. Por exemplo: “Realiza uma busca semântica no índice elastic-dev-docs para encontrar documentação interna de engenharia, manuais de operação e procedimentos de lançamento.”</p><p>Com isso configurado, a ferramenta está preparada para realizar uma busca semântica em nosso índice específico. Uma vez salvo, fica imediatamente disponível para ser servido.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" style="width: 100%;" /><p>Antes de conectar o dispositivo ao mundo exterior, você pode testá-lo diretamente na interface do usuário. Basta clicar no botão <strong>Testar</strong> para preencher manualmente os parâmetros, simulando o que o LLM fará, e inspecionar os resultados para confirmar se tudo está funcionando corretamente.</p><h3>Etapa 2: Conecte o Cursor ao servidor Elastic MCP</h3><p>O Elastic Agent Builder expõe automaticamente todas as ferramentas disponíveis por meio de um endpoint MCP seguro. Você pode encontrar o URL exclusivo do seu servidor na interface de Ferramentas do Kibana.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" style="width: 100%;" /><p>Para conectar ao Cursor, basta adicionar este URL ao seu arquivo de configuração, juntamente com uma chave de API Elastic para autenticação (<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">saiba como criar uma chave de API ES</a>). Utilizamos uma chave de API para autorização, pois isso garante que as ferramentas sejam executadas somente com as permissões que você concedeu, respeitando todas as suas regras de controle de acesso.</p><p>A configuração MCP em <code>~/.cursor/mcp.json</code> do Cursor se parece com isto:</p><pre><code>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana.kb.company.io/api/agent_builder/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "ApiKey <ELASTIC_API_KEY>"
      }
    }
  }
}</code></pre><p>Após salvar a configuração, você deverá ver a ferramenta de servidor Elastic Agent Builder MCP disponível no Cursor.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" style="width: 100%;" /><h3>Passo 3: pergunte à vontade!</h3><p>Com a conexão estabelecida, os agentes do Cursor agora podem invocar suas ferramentas personalizadas para responder às suas perguntas ou orientar o processo de geração de código.</p><p>Vamos fazer uma pergunta específica:</p><p><em>“Consulte os passos para liberar o serviço de rastreamento na documentação interna de engenharia da organização do Elasticsearch”</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" style="width: 100%;" /><p>Nos bastidores, a magia acontece:</p><ol><li>O agente Cursor decide a melhor forma de responder à sua pergunta e, em seguida, decide ligar para o <code>engineering_documentation_internal_search</code></li><li>Ela invoca a ferramenta com uma consulta em linguagem natural.</li><li>A ferramenta executa uma busca semântica no índice <code>elastic-dev-docs</code> e retorna os procedimentos mais relevantes e atualizados.</li></ol><p>Obtemos uma resposta precisa e confiável com base em nossa documentação interna, tudo isso sem precisar sair do editor de código. A experiência é perfeita e impactante.</p><h2>Sua vez de construir</h2><p>Agora você viu como usar o servidor MCP integrado no Elastic Agent Builder para estender seus assistentes de IA com acesso seguro aos seus dados privados. Fundamentar os modelos em suas próprias informações é fundamental para torná-los verdadeiramente úteis.</p><p>Recapitulando, abordamos as etapas principais:</p><ul><li>Escolher o protocolo certo para as suas necessidades (MCP).</li><li>Criação de uma ferramenta de base de conhecimento personalizada.</li><li>Conectar essa ferramenta a um assistente de IDE como o Cursor.</li></ul><p>Seus agentes e ferramentas não precisam mais estar desconectados de seu contexto mais valioso. Esperamos que este guia ajude você a criar fluxos de trabalho mais eficazes e orientados a dados. Boa construção!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Treinamento de modelos LTR no Elasticsearch com listas de julgamento baseadas em dados de comportamento do usuário.]]></title>
            <description><![CDATA[Aprenda como usar dados de UBI (Urban Business Information - Informação Baseada no Usuário) para criar listas de julgamento e automatizar o treinamento de seus modelos de Learning to Rank (LTR) no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/training-learning-to-rank-models-elasticsearch-ubi-data</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/training-learning-to-rank-models-elasticsearch-ubi-data</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
            <pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/762bf09c28829d626d42c2cfadc719e1dd618d1b-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Um grande desafio ao usar modelos <a href="https://www.elastic.co/docs/solutions/search/ranking/learning-to-rank-ltr"><strong><em>de aprendizado para classificação</em></strong></a> é criar uma <a href="https://www.elastic.co/search-labs/blog/judgment-lists"><strong><em>lista de julgamentos</em></strong></a> de alta qualidade para treinar o modelo. Tradicionalmente, esse processo envolve uma avaliação <strong><em>manual</em></strong> da relevância do documento de consulta para atribuir uma nota a cada um. Este é um processo lento, que não é escalável e é difícil de manter (imagine ter que atualizar manualmente uma lista com centenas de entradas).</p><p>E se pudéssemos usar interações reais de usuários com nosso aplicativo de busca para criar esses dados de treinamento? Utilizar dados de <a href="https://www.elastic.co/search-labs/blog/elasticsearch-plugin-user-behavior-insights"><strong><em>Renda Básica Universal (RBU)</em></strong></a> nos permite fazer exatamente isso. Criar um sistema automático capaz de capturar e usar nossas buscas, cliques e outras interações para gerar uma lista de julgamentos. Esse processo pode ser dimensionado e repetido com muito mais facilidade do que uma interação manual e tende a produzir melhores resultados. Neste blog, exploraremos como podemos consultar dados de UBI armazenados no Elasticsearch para calcular sinais relevantes e gerar um conjunto de dados de treinamento para um modelo <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction"><strong><em>LTR</em></strong></a> .</p><p><strong><em>Você pode encontrar o experimento completo <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git">aqui</a>.</em></strong></p><h2>Por que os dados de UBI podem ser úteis para treinar seu modelo LTR?</h2><p>Os dados UBI oferecem diversas vantagens em relação à anotação manual:</p><ul><li><strong>Volume:</strong> Dado que os dados da Renda Básica Universal (RBU) provêm de interações reais, podemos coletar muito mais dados do que poderíamos gerar manualmente. Isso pressupõe que tenhamos tráfego suficiente para gerar esses dados, é claro.</li><li><strong>Intenção real do usuário:</strong> Tradicionalmente, uma lista de julgamento manual provém de uma avaliação especializada dos dados disponíveis. Por outro lado, os dados da UBI refletem o comportamento real do usuário. Isso significa que podemos gerar melhores dados de treinamento que aprimorarão a precisão do nosso sistema de busca, pois ele se baseia em como os usuários realmente interagem com o seu conteúdo e encontram valor nele, em vez de suposições teóricas sobre o que deveria ser relevante.</li><li><strong>Atualizações contínuas:</strong> As listas de julgamentos precisam ser atualizadas periodicamente. Se criarmos essas listas a partir de dados da Renda Básica Universal (RBU), poderemos obter dados atuais que resultarão em listas de julgamento atualizadas.</li><li><strong>Relação custo-benefício:</strong> Sem o trabalho extra de criar manualmente uma lista de julgamento, o processo pode ser repetido de forma eficiente quantas vezes forem necessárias.</li><li><strong>Distribuição natural de consultas</strong>: os dados UBI representam consultas reais de usuários, o que pode gerar mudanças mais profundas. Por exemplo, nossos usuários utilizam linguagem natural para pesquisar em nosso sistema? Nesse caso, talvez devêssemos implementar uma abordagem de busca semântica ou de busca híbrida.</li></ul><p>No entanto, isso vem com alguns avisos:</p><ul><li><strong>Amplificação de viés: </strong>conteúdo popular tem maior probabilidade de receber cliques, simplesmente por ter mais visibilidade. Isso pode acabar amplificando os itens populares e possivelmente ofuscando opções melhores.</li><li><strong>Cobertura incompleta: </strong>O conteúdo novo não possui interações, portanto, pode ser difícil para ele aparecer em posições elevadas nos resultados. Consultas raras também podem não ter pontos de dados suficientes para criar dados de treinamento significativos.</li><li><strong>Variações sazonais:</strong> Se você espera que o comportamento do usuário mude drasticamente ao longo do tempo, os dados históricos podem não lhe dizer muito sobre o que é um bom resultado.</li><li><strong>Ambiguidade da tarefa:</strong> um clique nem sempre garante que o usuário encontrou o que procurava.</li></ul><h2>Cálculo das notas</h2><h3>Notas para treinamento LTR</h3><p>Para treinar modelos LTR, precisamos fornecer alguma representação numérica da relevância de um documento para uma consulta. Em nossa implementação, esse número é uma pontuação contínua que varia de 0,0 a 5,0+, onde pontuações mais altas indicam maior relevância.</p><p>Para demonstrar como funciona esse sistema de avaliação, considere este exemplo criado manualmente:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consulta</td><td style="border: 1px solid #ccc; padding: 4px;">Conteúdo do documento</td><td style="border: 1px solid #ccc; padding: 4px;">Nota</td><td style="border: 1px solid #ccc; padding: 4px;">Explicação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"Melhor receita de pizza"</td><td style="border: 1px solid #ccc; padding: 4px;">"Receita autêntica de massa de pizza italiana com fotos passo a passo"</td><td style="border: 1px solid #ccc; padding: 4px;">4.0</td><td style="border: 1px solid #ccc; padding: 4px;">Altamente relevante, exatamente o que o usuário está procurando.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"Melhor receita de pizza"</td><td style="border: 1px solid #ccc; padding: 4px;">"História da Pizza na Itália"</td><td style="border: 1px solid #ccc; padding: 4px;">1.0</td><td style="border: 1px solid #ccc; padding: 4px;">Ainda que relacionado ao assunto, trata-se de pizza, mas não é uma receita.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"Melhor receita de pizza"</td><td style="border: 1px solid #ccc; padding: 4px;">"Receita rápida de pizza em 15 minutos para iniciantes"</td><td style="border: 1px solid #ccc; padding: 4px;">3.0</td><td style="border: 1px solid #ccc; padding: 4px;">Relevante, um bom resultado, mas talvez não chegue a ser a "melhor" receita.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">"Melhor receita de pizza"</td><td style="border: 1px solid #ccc; padding: 4px;">"Guia de Manutenção Automotiva"</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">Completamente irrelevante, sem qualquer relação com a pergunta.</td>
                    </tr>
              </tbody>
            </table>
          <p>Como podemos ver aqui, a nota é uma representação numérica da relevância de um documento para nossa consulta de exemplo: &quot;melhor receita de pizza&quot;. Com essas pontuações, nosso modelo LTR pode aprender quais documentos devem ser apresentados em posições mais altas nos resultados.</p><p>A forma de calcular as notas é o ponto central do nosso conjunto de dados de treinamento. Existem <a href="https://www.elastic.co/search-labs/blog/judgment-lists">várias abordagens</a> para fazer isso, cada uma com seus pontos fortes e fracos. Por exemplo, poderíamos atribuir uma pontuação binária de 1 para relevante e 0 para irrelevante, ou poderíamos simplesmente contar o número de cliques em um documento resultante para cada consulta.</p><p>Neste post do blog, usaremos uma abordagem diferente, <strong><em>considerando o comportamento do usuário como entrada e calculando uma nota como saída</em></strong>. Também corrigiremos o viés que pode ocorrer devido ao fato de que resultados mais altos tendem a receber mais cliques, independentemente da relevância do documento.</p><h2>Cálculo das notas - Algoritmo COEC</h2><p>O algoritmo COEC (<a href="https://www.wsdm-conference.org/2010/proceedings/docs/p351.pdf">Clicks over Expected Clicks</a>) é uma metodologia para calcular notas de avaliação a partir dos cliques do usuário.<br/>Como já mencionamos, os usuários tendem a clicar nos resultados posicionados mais acima, mesmo que o documento não seja o mais relevante para a consulta; isso é chamado de <a href="https://eugeneyan.com/writing/position-bias/">Viés de Posição</a>. A ideia central por trás do uso do algoritmo COEC é que nem todos os cliques têm a mesma importância; um clique em um documento na posição 10 indica que o documento é muito mais relevante para a consulta do que um clique em um documento na posição 1. Citando o artigo de pesquisa sobre o algoritmo COEC (link acima):</p><p><em>“É sabido que a taxa de cliques (CTR) dos resultados de pesquisa ou anúncios diminui significativamente dependendo da posição dos resultados.”</em></p><p>Você pode ler mais sobre viés de posição <a href="https://www.researchgate.net/publication/200110550_An_experimental_comparison_of_click_position-bias_models">aqui</a>.</p><p>Para resolver isso com o algoritmo COEC, seguimos estes passos:</p><p><strong>1. Estabelecer linhas de base de posicionamento:</strong> Calculamos a taxa de cliques (CTR) para cada posição de pesquisa de 1 a 10. Isso significa que determinamos qual a porcentagem de usuários que normalmente clicam na posição 1, na posição 2 e assim por diante. Esta etapa captura a tendência natural de posicionamento dos usuários.<br/><br/>Calculamos a CTR usando:</p><div class="katex">CTRp=CpIp</div><p>Onde:</p><p>p = Posição. De 1 a 10<br/> Cp = Total de cliques (em qualquer documento) na posição p em todas as consultas<br/> Ip = Impressões totais: Quantas vezes um documento apareceu na posição p em todas as consultas.</p><p>Aqui, esperamos que posições mais altas recebam mais cliques.</p><p><strong>2.</strong> <strong>Calcular os cliques esperados (CE)</strong>:</p><p>Essa métrica estabelece quantos cliques um documento &quot;deveria&quot; ter recebido com base nas posições em que apareceu e na taxa de cliques (CTR) dessas posições. Calculamos o EC usando:</p><div class="katex">EC (for a document) = qQdCTRpos(d,q)</div><p>Onde:</p><p>Qd = Todas as consultas em que o documento d apareceu<br/> pos(d,q) = Posição do documento d nos resultados da consulta q</p><p>3. <strong>Contagem de cliques reais: </strong>Contamos o total real de cliques que um documento recebeu em todas as consultas em que apareceu, daqui em diante denominado <strong>A(d).</strong></p><p>4. <strong>Calcule a pontuação COEC:</strong> Esta é a razão entre os cliques reais (A(d)) e os cliques esperados (EC(d)):</p><div class="katex">COEC = A(d)EC(d)</div><p>Essa métrica normaliza o viés de posição da seguinte forma:</p><ul><li>Uma pontuação de 1,0 significa que o documento teve o desempenho exatamente como esperado, considerando as posições em que foi apresentado.</li><li>Uma pontuação acima de 1,0 significa que o documento teve um desempenho melhor do que o esperado, considerando suas posições. Portanto, este documento é mais relevante para a consulta.</li><li>Uma pontuação inferior a 1,0 significa que o documento teve um desempenho pior do que o esperado, considerando suas posições. Portanto, este documento é menos relevante para a consulta.</li></ul><p><strong><em>O resultado final é uma nota que reflete o que os usuários procuram, levando em consideração as expectativas baseadas na posição, extraídas de interações reais com nosso sistema de busca.</em></strong></p><h2>Implementação técnica</h2><p>Criaremos um script para gerar uma lista de julgamentos para treinar um modelo LTR.</p><p>A entrada para este script são os dados UBI indexados no Elastic (consultas e eventos).</p><p>O resultado é uma lista de julgamentos em um arquivo CSV gerado a partir desses documentos de Renda Básica Universal (RBU) usando o algoritmo COEC. Essa lista de julgamentos pode ser usada com <a href="https://www.elastic.co/search-labs/blog/elasticsearch-learning-to-rank-introduction">o Eland</a> para extrair características relevantes e treinar um modelo LTR.</p><h3>Início rápido</h3><p>Para gerar uma lista de julgamentos a partir dos dados de exemplo deste blog, você pode seguir estes passos:</p><p>1. Clone o repositório:</p><pre><code>git clone https://github.com/Alex1795/elastic-ltr-judgement_list-blog.git  
cd elastic-ltr-judgement_list-blog</code></pre><p>2. Instale as bibliotecas necessárias</p><p>Para este script, precisamos das seguintes bibliotecas:</p><ul><li><em>pandas</em>: para salvar a lista de julgamentos</li><li><em>elasticsearch</em>: Para obter os dados UBI da nossa implementação do Elasticsearch.</li></ul><p>Também precisamos do Python 3.11.</p><pre><code>pip install -r requirements.txt</code></pre><p>3. Atualize as variáveis de ambiente para sua implantação do Elasticsearch em um <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/.env-example">arquivo .env.</a></p><ul><li>ES_HOST</li><li>API_KEY</li></ul><p>Para adicionar as variáveis de ambiente, utilize:</p><pre><code>source .env</code></pre><p>4. Crie os índices ubi_queries e ubi_events e carregue os dados de exemplo. Execute o arquivo setup.py:</p><pre><code>python setup.py</code></pre><p>5. Execute o script Python:</p><pre><code>python judgement_list-generator.py</code></pre><p>Seguindo esses passos, você deverá ver um novo arquivo chamado judgment_list.csv com a seguinte aparência:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2531090131ac9fe3e4e1d79de9d156fc47a7825a-782x531.png" style="width: 100%;" /><p>Este script calcula as notas aplicando o algoritmo COEC discutido anteriormente, utilizando a função <strong>calculate_relevance_grade()</strong> mostrada abaixo.</p><h2>Arquitetura de dados</h2><h3>Consultas Ubi</h3><p>Nosso índice de consultas UBI contém informações sobre as consultas executadas em nosso sistema de busca. Este é um documento de exemplo:</p><pre><code>{
          "client_id": "client_002",
          "query": "italian pasta recipes",
          "query_attributes": {
            "search_type": "recipe",
            "category": "food",
            "cuisine": "italian"
          },
          "query_id": "q002",
          "query_response_id": "qr002",
          "query_response_object_ids": [
            "doc_011",
            "doc_012",
            "doc_013",
            "doc_014",
            "doc_015",
            "doc_016",
            "doc_017",
            "doc_018",
            "doc_019",
            "doc_020"
          ],
          "timestamp": "2024-08-14T11:15:00Z",
          "user_query": "italian pasta recipes"
        }</code></pre><p>Aqui podemos ver dados do usuário (client_id), dos resultados da consulta (query_response_object_ids) e da própria consulta (timestamp, user_query).</p><h3>Eventos de clique Ubi</h3><p>Nosso índice ubi_events contém dados de cada vez que um usuário clicou em um documento nos resultados. Este é um documento de exemplo:</p><pre><code>{
          "action_name": "click",
          "application": "recipe_search",
          "client_id": "client_001",
          "event_attributes": {
            "object": {
              "description": "Authentic Italian Pizza Dough Recipe with Step-by-Step Photos",
              "device": "desktop",
              "object_id": "doc_001",
              "position": {
                "ordinal": 1,
                "page_depth": 1
              },
              "user": {
                "city": "New York",
                "country": "USA",
                "ip": "192.168.1.100",
                "location": {
                  "lat": 40.7128,
                  "lon": -74.006
                },
                "region": "NY"
              }
            }
          },
          "message": "User clicked on document doc_001",
          "message_type": "click",
          "query_id": "q001",
          "timestamp": "2024-08-14T10:31:00Z",
          "user_query": "best pizza recipe"
        }</code></pre><h2>Script de geração de lista de julgamentos</h2><h3>Visão geral do roteiro</h3><p>Este script automatiza a geração da lista de julgamento usando dados UBI de consultas e eventos de clique armazenados no Elasticsearch. Ele executa estas tarefas:</p><ul><li>Busca e processa os dados UBI no Elasticsearch.</li><li>Correlaciona eventos de UBI com suas consultas.</li><li>Calcula o CTR para cada posição.</li><li>Calcula os cliques esperados (CE) para cada documento.</li><li>Contabiliza os cliques reais em cada documento.</li><li>Calcula a pontuação COEC para cada par consulta-documento.</li><li>Gera uma lista de julgamentos e a grava em um arquivo CSV.</li></ul><p>Vamos analisar cada função:</p><h3>conectar_ao_elasticsearch()</h3><pre><code>def connect_to_elasticsearch(host, api_key):
    """Create and return Elasticsearch client"""
    try:
        es = Elasticsearch(
            hosts=[host],
            api_key=api_key,
            request_timeout=60
        )
        # Test the connection
        if es.ping():
            print(f"✓ Successfully connected to Elasticsearch at {host}")
            return es
        else:
            print("✗ Failed to connect to Elasticsearch")
            return None
    except Exception as e:
        print(f"✗ Error connecting to Elasticsearch: {e}")
        return None</code></pre><p>Essa função retorna um objeto cliente Elasticsearch usando o host e a chave da API.</p><h3>buscar_dados_ubi()</h3><pre><code>def fetch_ubi_data(es_client: Elasticsearch, queries_index: str, events_index: str,
                   size: int = 10000) -> Tuple[List[Dict], List[Dict]]:
    """
    Fetch UBI queries and events data from Elasticsearch indices.

    Args:
        es_client: Elasticsearch client
        queries_index: Name of the UBI queries index
        events_index: Name of the UBI events index
        size: Maximum number of documents to fetch

    Returns:
        Tuple of (queries_data, events_data)
    """
    logger.info(f"Fetching data from {queries_index} and {events_index}")

    # Fetch queries with error handling
    try:
        queries_response = es_client.search(
            index=queries_index,
            body={
                "query": {"match_all": {}},
                "size": size
            }
        )
        queries_data = [hit['_source'] for hit in queries_response['hits']['hits']]
        logger.info(f"Fetched {len(queries_data)} queries")

    except Exception as e:
        logger.error(f"Error fetching queries from {queries_index}: {e}")
        raise

    # Fetch events (only click events for now) with error handling
    try:
        events_response = es_client.search(
            index=events_index,
            body={
                "query": {
                    "term": {"message_type.keyword": "CLICK_THROUGH"}
                },
                "size": size
            }
        )
        events_data = [hit['_source'] for hit in events_response['hits']['hits']]
        logger.info(f"Fetched {len(events_data)} click events")

    except Exception as e:
        logger.error(f"Error fetching events from {events_index}: {e}")
        raise

    logger.info(f"Data fetch completed successfully - Queries: {len(queries_data)}, Events: {len(events_data)}")

    return queries_data, events_data</code></pre><p>Esta função é a camada de extração de dados; ela se conecta ao Elasticsearch para buscar consultas UBI usando uma consulta match_all e filtra os eventos UBI para obter apenas os eventos &#x27;CLICK_THROUGH&#x27;.</p><h3>processar_dados_ubi()</h3><pre><code>def process_ubi_data(queries_data: List[Dict], events_data: List[Dict]) -> pd.DataFrame:
    """
    Process UBI data and generate judgment list.

    Args:
        queries_data: List of query documents from UBI queries index
        events_data: List of event documents from UBI events index

    Returns:
        DataFrame with judgment list (qid, docid, grade, keywords)
    """
    logger.info("Processing UBI data to generate judgment list")

    # Group events by query_id
    clicks_by_query = {}
    for event in events_data:
        query_id = event['query_id']
        if query_id not in clicks_by_query:
            clicks_by_query[query_id] = {}

        # Extract clicked document info
        object_id = event['event_attributes']['object']['object_id']
        position = event['event_attributes']['object']['position']['ordinal']

        clicks_by_query[query_id][object_id] = {
            'position': position,
            'timestamp': event['timestamp']
        }

    judgment_list = []

    # Process each query
    for query in queries_data:
        query_id = query['query_id']
        user_query = query['user_query']
        document_ids = query['query_response_object_ids']

        # Get clicks for this query
        query_clicks = clicks_by_query.get(query_id, {})

        # Generate judgment for each document shown
        for doc_id in document_ids:
            grade = calculate_relevance_grade(doc_id, query_clicks, document_ids, queries_data, events_data)

            judgment_list.append({
                'qid': query_id,
                'docid': doc_id,
                'grade': grade,
                'query': user_query
            })

    df = pd.DataFrame(judgment_list)
    logger.info(f"Generated {len(df)} judgment entries for {df['qid'].nunique()} unique queries")

    return df</code></pre><p>Esta função é responsável pela geração da lista de julgamentos. O processamento dos dados UBI começa por associar eventos e consultas UBI. Em seguida, chama a função calculate_relevance_grade() para cada par documento-consulta, a fim de obter as entradas para a lista de julgamento. Por fim, retorna a lista resultante como um dataframe do pandas.</p><h3>calcular_nota_de_relevância()</h3><pre><code>def calculate_relevance_grade(document_id: str, clicks_data: Dict,
                              query_response_ids: List[str], all_queries_data: List[Dict] = None,
                              all_events_data: List[Dict] = None) -> float:
    """
    Calculate COEC (Click Over Expected Clicks) relevance score for a document.

    Args:
        document_id: ID of the document
        clicks_data: Dictionary of clicked documents with their positions for current query
        query_response_ids: List of document IDs shown in search results (ordered by position)
        all_queries_data: All queries data for calculating position CTR averages
        all_events_data: All events data for calculating position CTR averages

    Returns:
        COEC relevance score (continuous value, typically 0.0 to 5.0+)
    """

    # If no global data provided, fall back to simple position-based grading
    if all_queries_data is None or all_events_data is None:
        logger.warning("No global data provided, falling back to position-based grading")
        # Simple fallback logic
        if document_id in clicks_data:
            position = clicks_data[document_id]['position']
            if position > 3:
                return 4.0
            elif position >= 1 and position <= 3:
                return 3.0
        if document_id in query_response_ids:
            position = query_response_ids.index(document_id) + 1
            if position <= 5:
                return 2.0
            elif position >= 6 and position <= 10:
                return 1.0
        return 0.0

    # Calculate rank-aggregated click-through rates
    position_ctr_averages = {}
    position_impression_counts = {}
    position_click_counts = {}

    # Initialize counters
    for pos in range(1, 11):  # Positions 1-10
        position_impression_counts[pos] = 0
        position_click_counts[pos] = 0

    # Count impressions (every document shown contributes)
    for query in all_queries_data:
        for i, doc_id in enumerate(query['query_response_object_ids'][:10]):  # Top 10 positions
            position = i + 1
            position_impression_counts[position] += 1

    # Count clicks by position
    for event in all_events_data:
        if event.get('action_name') == 'click':
            position = event['event_attributes']['object']['position']['ordinal']
            if position <= 10:
                position_click_counts[position] += 1

    # Calculate average CTR per position
    for pos in range(1, 11):
        if position_impression_counts[pos] > 0:
            position_ctr_averages[pos] = position_click_counts[pos] / position_impression_counts[pos]
        else:
            position_ctr_averages[pos] = 0.0

    # Calculate expected clicks for this specific document
    expected_clicks = 0.0

    # Count how many times this document appeared at each position for any query
    for query in all_queries_data:
        if document_id in query['query_response_object_ids']:
            position = query['query_response_object_ids'].index(document_id) + 1
            if position <= 10:
                expected_clicks += position_ctr_averages[position]

    # Count total actual clicks for this document across all queries
    actual_clicks = 0
    for event in all_events_data:
        if (event.get('action_name') == 'click' and
                event['event_attributes']['object']['object_id'] == document_id):
            actual_clicks += 1

    # Calculate COEC score
    if expected_clicks > 0:
        coec_score = actual_clicks / expected_clicks
    else:
        coec_score = 0.0

    logger.debug(
        f"Document {document_id}: {actual_clicks} clicks / {expected_clicks:.3f} expected = {coec_score:.3f} COEC")

    return coec_score</code></pre><p>Esta é a função que implementa o algoritmo COEC. Ele calcula a CTR para cada posição, depois compara os cliques reais para um par documento-consulta e, finalmente, calcula a pontuação COEC real para cada um.</p><h3>gerar_estatísticas_de_julgamento()</h3><pre><code>def generate_judgment_statistics(df: pd.DataFrame) -> Dict:
    """Generate statistics about the judgment list."""
    stats = {
        'total_judgments': len(df),
        'unique_queries': df['qid'].nunique(),
        'unique_documents': df['docid'].nunique(),
        'grade_distribution': df['grade'].value_counts().to_dict(),
        'avg_judgments_per_query': len(df) / df['qid'].nunique() if df['qid'].nunique() > 0 else 0,
        'queries_with_clicks': len(df[df['grade'] > 1]['qid'].unique()),
        'click_through_rate': len(df[df['grade'] > 1]) / len(df) if len(df) > 0 else 0
    }
    return stats</code></pre><p>Ele gera estatísticas úteis a partir da lista de julgamentos, como o total de consultas, o total de documentos únicos ou a distribuição de notas. Esta informação é meramente informativa e não altera a lista de julgamentos resultante.</p><h2>Resultados e impacto</h2><p>Seguindo as instruções da seção Início rápido, você deverá obter um arquivo CSV contendo uma lista de julgamentos com 320 entradas (você pode ver um <a href="https://github.com/Alex1795/elastic-ltr-judgement_list-blog/blob/main/judgment_list.csv">exemplo de saída</a> no repositório). Com estes campos:</p><ul><li>qid: ID único da consulta</li><li>docid: identificador único para um documento resultante</li><li>nota: a nota calculada para o par consulta-documento.</li><li>consulta: A consulta do usuário</li></ul><p> Vejamos os resultados da pesquisa “receitas italianas”:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">qid</td><td style="border: 1px solid #ccc; padding: 4px;">docid</td><td style="border: 1px solid #ccc; padding: 4px;">nota</td><td style="border: 1px solid #ccc; padding: 4px;">Consulta</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">receita_básica_de_massa</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">receita_pizza_margherita</td><td style="border: 1px solid #ccc; padding: 4px;">3,333333</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">guia_de_receitas_de_risoto</td><td style="border: 1px solid #ccc; padding: 4px;">10.0</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">receita_croissant_francês</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">receita_paella_espanhola</td><td style="border: 1px solid #ccc; padding: 4px;">0,0</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">q1-receitas-italianas</td><td style="border: 1px solid #ccc; padding: 4px;">receita_moussaka_grega</td><td style="border: 1px solid #ccc; padding: 4px;">1,875</td><td style="border: 1px solid #ccc; padding: 4px;">receitas italianas</td>
                    </tr>
              </tbody>
            </table>
          <p>Podemos ver pelos resultados que para a consulta “receitas italianas”:</p><ul><li>A receita de risoto é definitivamente o melhor resultado para a pesquisa, recebendo 10 vezes mais cliques do que o esperado.</li><li>A pizza Margherita também é um ótimo resultado.</li><li>A mousaka grega (surpreendentemente) também obteve um bom resultado e teve um desempenho melhor do que sua posição nos resultados sugeriria. Isso significa que alguns usuários que procuravam receitas italianas se interessaram por esta receita em vez desta. Talvez esses usuários estejam interessados em pratos mediterrâneos em geral. Em suma, isso nos indica que esse poderia ser um bom resultado para ser apresentado entre as outras duas partidas &quot;melhores&quot; que discutimos anteriormente.</li></ul><h2>Conclusão</h2><p>Utilizar dados UBI nos permite automatizar o treinamento de modelos LTR, criando listas de julgamento de alta qualidade a partir de nossos próprios usuários. Os dados do UBI fornecem um grande conjunto de dados que reflete como nosso sistema de busca está sendo usado. Ao usar o algoritmo COEC para gerar as notas, levamos em consideração o viés inerente e, ao mesmo tempo, refletimos o que um usuário considera um resultado melhor. O método descrito aqui pode ser aplicado a casos de uso reais para proporcionar uma melhor experiência de busca que evolua com as tendências reais de uso.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Avaliação de agentes de IA: como a Elastic testa frameworks de agentes]]></title>
            <description><![CDATA[Saiba como avaliamos e testamos as alterações em um sistema de agentes antes de liberá-las para os usuários da Elastic, garantindo resultados precisos e verificáveis.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-agent-evaluation-elastic</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-agent-evaluation-elastic</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Susan Chang, Abhimanyu Anand]]></dc:creator>
            <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e681aab6ee77dfef38dc95e36c8535dfe7bcc105-721x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<h2>Introdução</h2><p>No Elastic Stack, existem muitos aplicativos agentivos baseados em LLM, como o futuro Elastic AI Agent no<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder"> Agent Builder</a> (atualmente em versão de pré-visualização técnica) e <a href="https://www.elastic.co/docs/solutions/security/ai/attack-discovery">o Attack Discovery</a> (<a href="https://www.elastic.co/blog/whats-new-elastic-security-9-0-0">disponível para o público geral</a> nas versões 8.18 e 9.0+), com mais em desenvolvimento. Durante o desenvolvimento, e mesmo após a implementação, é importante responder a estas perguntas:</p><ul><li>Como podemos estimar a qualidade das respostas dessas aplicações de IA?</li><li>Se fizermos uma alteração, como podemos garantir que ela seja realmente uma melhoria e não cause deterioração na experiência do usuário?</li><li>Como podemos testar esses resultados de forma fácil e repetível?</li></ul><p>Diferentemente dos testes de software tradicionais, a avaliação de aplicações de IA generativa envolve métodos estatísticos, análises qualitativas minuciosas e uma compreensão profunda dos objetivos do usuário.</p><p>Este artigo detalha o processo que a equipe de desenvolvimento da Elastic utiliza para realizar avaliações, garantir a qualidade das alterações antes da implantação e monitorar o desempenho do sistema. Nosso objetivo é garantir que cada mudança seja respaldada por evidências, resultando em resultados confiáveis e verificáveis. Parte desse processo está integrada diretamente ao Kibana, refletindo nosso compromisso com a transparência como parte de nossa filosofia de código aberto. Ao compartilhar abertamente partes de nossos dados e métricas de avaliação, buscamos fomentar a confiança da comunidade e fornecer uma estrutura clara para qualquer pessoa que desenvolva agentes de IA ou utilize nossos produtos.</p><h2>Exemplos de produtos</h2><p>Os métodos utilizados neste documento serviram de base para a forma como iteramos e aprimoramos soluções como o Attack Discovery e o Elastic AI Agent. Uma breve introdução aos dois, respectivamente:</p><h3>Descoberta de ataques da Elastic Security</h3><p>A descoberta de ataques utiliza LLMs para identificar e resumir sequências de ataques no Elastic. Com base nos alertas do Elastic Security em um determinado período (padrão de 24 horas), o fluxo de trabalho automatizado do Attack Discovery identificará automaticamente se ocorreram ataques, além de informações importantes, como quais hosts ou usuários foram comprometidos e quais alertas contribuíram para essa conclusão.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" style="width: 100%;" /><p></p><p>O objetivo é que a solução baseada em LLM produza um resultado pelo menos tão bom quanto o de um ser humano.</p><h3>Agente de IA Elástico</h3><p>O <strong>Elastic Agent Builder</strong> é a nossa nova plataforma para criar agentes de IA sensíveis ao contexto que aproveitam todos os nossos recursos de busca. Ele vem com o <strong>Elastic AI Agent</strong>, um agente pré-construído de uso geral, projetado para ajudar os usuários a entender e obter respostas a partir de seus dados por meio de interação conversacional.</p><p>O agente consegue isso identificando automaticamente informações relevantes no Elasticsearch ou em bases de conhecimento conectadas e utilizando um conjunto de ferramentas pré-construídas para interagir com elas. Isso permite que o Elastic AI Agent responda a uma ampla gama de consultas de usuários, desde perguntas e respostas simples sobre um único documento até solicitações complexas que exigem agregação e buscas de uma ou várias etapas em diversos índices.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" style="width: 100%;" /><h2>Medindo melhorias por meio de experimentos</h2><p>No contexto de agentes de IA, um experimento é uma mudança estruturada e testável no sistema, projetada para melhorar o desempenho em dimensões bem definidas (por exemplo, utilidade, correção, latência). O objetivo é responder de forma definitiva: &quot;Se incorporarmos essa alteração, podemos garantir que ela representa uma melhoria real e não prejudicará a experiência do usuário?&quot;</p><p>A maioria dos experimentos que realizamos geralmente inclui:</p><ul><li><strong>Uma hipótese:</strong> uma afirmação específica e falseável. <em>Exemplo:</em> “Adicionar acesso a uma ferramenta de descoberta de ataques melhora a precisão das consultas relacionadas à segurança.”</li><li><strong>Critérios de sucesso:</strong> Limiares claros que definem o que significa &quot;sucesso&quot;. <em>Exemplo:</em> “Melhoria de 5% na pontuação de correção no conjunto de dados de segurança, sem degradação em outros locais.”</li><li><strong>Plano de avaliação:</strong> Como medimos o sucesso (métricas, conjuntos de dados, método de comparação)</li></ul><p>Um experimento bem-sucedido é um processo sistemático de investigação. Toda alteração, desde um pequeno ajuste de um prompt até uma grande mudança arquitetônica, segue estes sete passos para garantir que os resultados sejam significativos e acionáveis:</p><ul><li>Etapa 1: Identificar o problema</li><li>Etapa 2: Definir métricas</li><li>Etapa 3: Formule uma hipótese clara</li><li>Etapa 4: Preparar o conjunto de dados de avaliação</li><li>Etapa 5: Execute o experimento</li><li>Etapa 6: Analisar resultados + iterar</li><li>Etapa 7: Tome uma decisão e documente-a.</li></ul><p>Um exemplo dessas etapas é ilustrado na <em>Figura 1</em>. As subseções a seguir explicarão cada etapa, e detalharemos os aspectos técnicos de cada etapa em documentos futuros.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" style="width: 100%;" /><h2>Passo a passo com exemplos reais do Elastic</h2><h3>Etapa 1: Identificar o problema</h3><p><em>Qual é exatamente o problema que essa mudança visa resolver?</em></p><p>Exemplo de detecção de ataques: os resumos são ocasionalmente incompletos ou atividades benignas são erroneamente sinalizadas como ataques (falsos positivos).</p><p>Exemplo de agente de IA elástica: a seleção de ferramentas do agente, especialmente para consultas analíticas, é subótima e inconsistente, muitas vezes levando à escolha da ferramenta errada. Isso, por sua vez, aumenta os custos dos tokens e a latência.</p><h3>Etapa 2: Definir métricas</h3><p><em>Torne o problema mensurável, para que possamos comparar uma mudança com o estado atual.</em></p><p>As métricas comuns incluem <a href="https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall">precisão e revocação</a>, <a href="https://en.wikipedia.org/wiki/Semantic_similarity">similaridade semântica</a>, factualidade, e assim por diante. Dependendo do caso de uso, utilizamos verificações de código para calcular as métricas, como a correspondência de IDs de alerta ou URLs recuperados corretamente, ou técnicas como LLM-as-judge para respostas mais livres.</p><p>Abaixo estão alguns exemplos (<em>lista não exaustiva</em>) de métricas usadas nos experimentos:</p><p><strong>Descoberta de ataques</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Métrica</td><td style="border: 1px solid #ccc; padding: 4px;">Descrição</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Precisão e memorização</td><td style="border: 1px solid #ccc; padding: 4px;">Compare os IDs de alerta entre as saídas reais e esperadas para medir a precisão da detecção.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Semelhança</td><td style="border: 1px solid #ccc; padding: 4px;">Utilize o BERTScore para comparar a similaridade semântica do texto de resposta.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Factualidade</td><td style="border: 1px solid #ccc; padding: 4px;">Os principais indicadores de comprometimento (IOCs) estão presentes? As táticas MITRE (taxonomia de ataques do setor) estão corretamente representadas?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consistência da cadeia de ataque</td><td style="border: 1px solid #ccc; padding: 4px;">Compare o número de descobertas para verificar se houve superestimação ou subestimação da notificação do ataque.</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Agente de IA Elástico</strong></p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Métrica</td><td style="border: 1px solid #ccc; padding: 4px;">Descrição</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Precisão e memorização</td><td style="border: 1px solid #ccc; padding: 4px;">Comparar os documentos/informações recuperados pelo agente para responder a uma consulta do usuário com as informações ou documentos realmente necessários para responder à consulta, a fim de medir a precisão da recuperação de informações.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Factualidade</td><td style="border: 1px solid #ccc; padding: 4px;">Os principais fatos necessários para responder à consulta do usuário estão presentes? Os fatos estão na ordem correta para questões processuais?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Relevância da resposta</td><td style="border: 1px solid #ccc; padding: 4px;">A resposta contém informações periféricas ou não relacionadas à consulta do usuário?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Completude da resposta</td><td style="border: 1px solid #ccc; padding: 4px;">A resposta atende a todas as partes da consulta do usuário? A resposta contém todas as informações presentes na verdade fundamental?</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Validação ES|QL</td><td style="border: 1px solid #ccc; padding: 4px;">O código ES|QL gerado está sintaticamente correto? É funcionalmente idêntico ao ES|QL original?</td>
                    </tr>
              </tbody>
            </table>
          <h3>Etapa 3: Formule uma hipótese clara</h3><p><em>Estabeleça critérios de sucesso claros usando o problema e as métricas definidas acima.</em></p><p>Exemplo de agente de IA elástico:</p><ol><li>Implementar <strong>alterações nas descrições das ferramentas relevance_search e nl_search para definir claramente suas funções e casos de uso específicos</strong>.</li><li>Prevemos que <strong>melhoraremos</strong> <strong>a precisão da invocação de nossa ferramenta</strong> em <strong>25%</strong>.</li><li>Verificaremos se isso representa um saldo positivo, garantindo que não haja impacto negativo em outras métricas, por exemplo... <strong>factualidade e completude</strong>.</li><li>Acreditamos que isso funcionará porque <strong>descrições precisas das ferramentas ajudarão o agente a selecionar e aplicar com mais exatidão a ferramenta de busca mais adequada para diferentes tipos de consulta, reduzindo o uso incorreto e melhorando a eficácia geral da busca</strong>.</li></ol><h3>Etapa 4: Preparar o conjunto de dados de avaliação</h3><p><em>Para medir o desempenho do sistema, utilizamos conjuntos de dados que capturam cenários do mundo real.</em></p><p>Dependendo do tipo de avaliação que estivermos realizando, podemos precisar de diferentes formatos de dados, como dados brutos inseridos em um LLM (por exemplo, cenários de ataque para descoberta de ataques) e resultados esperados. Se o aplicativo for um chatbot, as entradas podem ser consultas do usuário e as saídas podem ser respostas corretas do chatbot, links corretos que ele deveria ter recuperado e assim por diante.</p><p>Exemplo de descoberta de ataques:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10 novos cenários de ataque</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">8 episódios de Oh My Malware (ohmymalware.com)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">4 cenários de múltiplos ataques (criados pela combinação de ataques nas duas primeiras categorias)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">3 cenários benignos</td>
                    </tr>
              </tbody>
            </table>
          <p>Exemplo de conjunto de dados para avaliação de agentes de IA elástica (<a href="https://github.com/elastic/kibana/blob/main/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/evals/kb/kb.spec.ts">Link para o conjunto de dados do Kibana</a>):</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">14 Índices que utilizam conjuntos de dados de código aberto para simular múltiplas fontes em KB.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">5 tipos de consulta (analítica, recuperação de texto, híbrida…)</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">7 tipos de intenção de consulta (procedimental, factual - classificação, investigativa; …)</td>
                    </tr>
              </tbody>
            </table>
          <h3>Etapa 5: Execute o experimento</h3><p>Execute o experimento gerando respostas tanto do agente existente quanto da versão modificada em relação ao conjunto de dados de avaliação. Calcule métricas como a veracidade factual (ver passo 2).</p><p>Combinamos diversas avaliações com base nas métricas exigidas na Etapa 2:</p><ul><li>Avaliação baseada em regras (por exemplo, (Use Python/TypeScript para verificar se o arquivo .json é válido)</li><li>LLM como juiz (consultar um LLM separado para verificar se uma resposta é factualmente consistente com um documento original)</li><li>Revisão com intervenção humana para verificações de qualidade e nuances.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" style="width: 100%;" /><h3>Etapa 6: Analisar resultados + iterar</h3><p>Agora que temos as métricas, vamos analisar os resultados. <em><span style="text-decoration:underline">Mesmo que os resultados atendam aos critérios de sucesso definidos na etapa 3, ainda faremos uma revisão humana antes de incorporar a alteração à produção</span></em>; se os resultados não atenderem aos critérios, iteraremos e corrigiremos os problemas e, em seguida, executaremos as avaliações na nova alteração.</p><p>Prevemos que serão necessárias algumas iterações para encontrarmos a melhor alteração antes de a consolidarmos. Assim como é feito executar testes de software locais antes de enviar uma alteração, as avaliações offline podem ser executadas com alterações locais ou com várias alterações propostas. Automatizar o salvamento de resultados experimentais, pontuações compostas e visualizações é útil para agilizar a análise.</p><h3>Etapa 7: Tome uma decisão e documente-a.</h3><p>Com base em uma estrutura de decisão e critérios de aceitação, decida sobre a incorporação da alteração e documente o experimento. A tomada de decisões é multifacetada e pode considerar fatores que vão além do conjunto de dados de avaliação, como verificar cenários de regressão em outros conjuntos de dados ou ponderar o custo-benefício de uma mudança proposta.</p><p>Exemplo: Após testar e comparar algumas iterações, escolha a alteração com a melhor pontuação para enviar aos gerentes de produto e outras partes interessadas relevantes para aprovação. Anexe os resultados das etapas anteriores para auxiliar na tomada de decisão. Para mais exemplos sobre a descoberta de ataques, consulte <a href="https://www.elastic.co/blog/elastic-security-generative-ai-features">Nos bastidores dos recursos de IA generativa do Elastic Security</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" style="width: 100%;" /><h2>Conclusão</h2><p>Neste blog, descrevemos o processo completo de um fluxo de trabalho de experimento, ilustrando como avaliamos e testamos as alterações em um sistema de agentes antes de disponibilizá-las aos usuários da Elastic. Também fornecemos alguns exemplos de como aprimorar fluxos de trabalho baseados em agentes no Elastic. Em publicações subsequentes no blog, detalharemos diferentes etapas, como criar um bom conjunto de dados, projetar métricas confiáveis e tomar decisões quando várias métricas estão envolvidas.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Conectando agentes elásticos ao Gemini Enterprise via protocolo A2A]]></title>
            <description><![CDATA[Aprenda a usar o Agent Builder para expor seu Elastic Agent personalizado a serviços externos como o Gemini Enterprise com o protocolo A2A.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jedr Blaszyk, Valerio Arvizzigno, Joe McElroy]]></dc:creator>
            <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>O Elastic Agent Builder</strong> é um conjunto de funcionalidades para criar agentes de IA orientados a dados diretamente no Elasticsearch. Em publicações anteriores desta <a href="https://www.elastic.co/search-labs/blog/series/context-aware-ai-agentic-workflows-with-elastic">série</a>, demonstramos como equipar agentes personalizados com ferramentas para executar tarefas complexas e fornecer-lhes um conjunto de instruções personalizadas para orientar seu comportamento.</p><p>Mas e se você quiser usar seus agentes personalizados com os aplicativos e ferramentas de produtividade que você já utiliza?</p><p>É aí que entra o <strong>protocolo Agente-para-Agente (A2A)</strong> . A2A é um <a href="https://github.com/a2aproject/A2A">padrão aberto</a> de interoperabilidade, permitindo que agentes de diferentes plataformas se comuniquem e colaborem. E nós o integramos diretamente ao Elastic Agent Builder.</p><p>Hoje, vamos mostrar como pegar um agente personalizado que você criou e expô-lo a outros serviços, especificamente, <strong>ao Gemini Enterprise </strong>(antigo Agentspace).</p><h2>O poder dos padrões abertos: por que a abordagem A2A é importante</h2><p>Na postagem do blog <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">&quot;Seu primeiro Elastic Agent&quot;</a>, mostramos como criar agentes personalizados, como um agente <em>de Assistente Financeiro</em> com acesso seguro aos seus dados de mercado. Mas seu valor é limitado se você não puder disponibilizar suas informações em outros ambientes, como o Gemini Enterprise, sem refazer todo o seu trabalho.</p><p>Esse desafio de interoperabilidade é o que impede o avanço da IA ativa. Os agentes precisam de uma linguagem comum para se comunicarem entre plataformas, e essa é precisamente a função do protocolo A2A. Ela fornece uma camada de comunicação padrão que não só permite a interação direta com o agente, como também abre caminho para um futuro em que agentes especializados em toda a organização possam colaborar e compartilhar informações.</p><p>Para tornar isso possível, o Elastic Agent Builder oferece suporte nativo ao protocolo A2A por meio de dois endpoints padrão para todos os seus agentes:</p><ol><li><strong>O endpoint do cartão do agente (<code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code>) - </strong>Este funciona como o cartão de visita personalizado do seu agente. Ele fornece metadados sobre seu agente (nome, descrição, capacidades, etc.) para qualquer serviço compatível com A2A.</li><li><strong>O ponto final do protocolo A2A (<code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code>)</strong> - Este é o canal de comunicação. Outros agentes enviam suas solicitações para cá, e seu agente as processa e retorna uma resposta, tudo seguindo a <a href="https://a2a-protocol.org/latest/specification/">especificação do protocolo A2A</a>.</li></ol><h2>Teste seu agente com o inspetor A2A.</h2><p>Antes de conectar nosso agente a um sistema de produção, é bom verificar se a comunicação está funcionando corretamente. A maneira mais fácil de fazer isso é com o <strong>A2A Inspector</strong>, uma ferramenta projetada especificamente para testar e depurar integrações A2A.</p><p>Colocar o inspetor em funcionamento é simples. Você pode clonar o repositório <a href="https://github.com/a2aproject/a2a-inspector">a2a-inspector</a> e seguir as instruções do arquivo README para <a href="https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#3-run-the-application">executar o aplicativo</a>. Uma vez iniciado, o UI está disponível por padrão em <code>http://localhost:5001/</code>.</p><p>Para conectar o A2A Inspector ao seu agente, você precisará fornecer duas informações essenciais:</p><ul><li>URL do Cartão do Agente: Este é o endpoint que descreve o seu agente. Para o <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">agente Assistente Financeiro da nossa postagem anterior</a>, este URL seria <code>{your-kibana-url}/api/agent_builder/a2a/financial_assistant.json</code>.</li><li>Cabeçalho de autenticação: Usaremos uma chave de API padrão para autenticação.</li></ul><p>Após inserir esses detalhes na interface do inspetor, você poderá se conectar e começar a conversar com seu agente imediatamente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" style="width: 100%;" /><p>Essa validação simples nos dá a certeza de que nosso agente está configurado corretamente e pronto para a próxima etapa.</p><h2>Entre ao vivo! Seu agente personalizado na Gemini Enterprise</h2><p>Agora vem a parte emocionante: dar vida ao nosso agente de consultoria financeira personalizado dentro do Gemini Enterprise (antigo Agentspace). Essa integração é viabilizada pelo <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-ai-agent">Elastic AI Agent, que está disponível no Google Cloud Marketplace</a>.</p><p>Uma vez conectado, o Gemini Enterprise usa o protocolo A2A para se comunicar diretamente com seu agente. É aqui que o verdadeiro poder da interoperabilidade se destaca: os usuários agora podem acessar insights profundos e baseados em dados do seu agente Elasticsearch personalizado sem precisar sair do ambiente familiar. Você pode ver seu agente elástico personalizado na lista de agentes:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" style="width: 100%;" /><p>Imagine um usuário do Gemini Enterprise perguntando:</p><p><em>&quot;Estou preocupado com o sentimento do mercado. Você pode me mostrar quais dos nossos clientes estão mais vulneráveis a notícias negativas?</em>&quot;</p><p>Nos bastidores, o Gemini Enterprise encaminha essa consulta por meio do protocolo A2A para o seu Elastic Agent personalizado. Seu agente então utiliza suas ferramentas especializadas para consultar seus dados, formular uma resposta e enviá-la de volta. Para o usuário final, a experiência é perfeita.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" style="width: 100%;" /><p>E não para por aqui! A resposta obtida com o agente elástico agora pode ser usada como contexto para suas próximas perguntas, que podem acionar um agente especializado diferente (por exemplo, seu agente de plataforma de investimentos para ajustar a exposição a empresas listadas). Tudo isso sem sair da sua barra de pesquisa.</p><p>Com seus agentes Elastic implantados no Gemini Enterprise com A2A, você pode unificar acesso, orquestração e fluxos de trabalho, eliminando atritos entre IA, pesquisa e sistemas corporativos, oferecendo uma interface de usuário única onde os usuários interagem com seus dados e ferramentas — tudo em contexto. Para os usuários, isso significa menos troca de ferramentas e assistentes de IA mais intuitivos e capazes. Para as organizações, isso significa governança coerente, escalabilidade e interoperabilidade integradas.</p><h2>Sua vez de construir</h2><p>Agora você tem as ferramentas para disponibilizar seus Agentes Elásticos em qualquer lugar. Ao aproveitar o protocolo aberto A2A, você pode ampliar o alcance de seus agentes personalizados e orientados a dados.</p><p>Neste post, apresentamos os principais passos:</p><ul><li>Exponha seu agente por meio do cartão de agente A2A e dos endpoints do protocolo.</li><li>Testando a conexão com o A2A Inspector.</li><li>Integrar seu agente em tempo real a um serviço externo como o Gemini Enterprise do Google.</li></ul><p>Seus agentes não precisam mais ficar isolados. Estamos ansiosos para ver os sistemas poderosos e interconectados que vocês criarão. Boa construção!</p><p>A maneira mais fácil de começar é com sua avaliação gratuita do Elastic Cloud no <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace.</a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Experimentos para aprimorar ferramentas de IA Agética para Elasticsearch]]></title>
            <description><![CDATA[Saiba como aprimoramos os fluxos de trabalho de agentes de IA para Elasticsearch por meio de experimentos iterativos, combinando recuperadores lineares, busca híbrida e semantic_text para otimização RAG escalável.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-agent-builder-experiments-performance</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-agent-builder-experiments-performance</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sean Story]]></dc:creator>
            <pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42903c869e034674b30bb36013345aaa97f6608b-1184x864.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Assim como todo mundo hoje em dia, aqui na Elastic, estamos investindo pesado em Chat, Agentes e RAG. Na área de Busca, temos trabalhado recentemente em um Construtor de Agentes e um Registro de Ferramentas, tudo com o intuito de tornar trivial a interação com seus dados no Elasticsearch.</p><p>Leia o <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">artigo &quot;Building AI Agentic Workflows with Elasticsearch&quot;</a> para obter mais informações sobre o panorama geral desse projeto, ou <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">&quot;Your First Elastic Agent: From a Single Query to a AI-Powered Chat&quot;</a> para uma introdução mais prática.</p><p>Neste blog, porém, vamos nos aprofundar um pouco em uma das primeiras coisas que acontecem quando você começa a conversar e apresentar algumas das melhorias recentes que implementamos.</p><h2>O que está acontecendo aqui?</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/25a24055a166d7d6ba81d80aa35cb97163662e23-1600x443.png" style="width: 100%;" /><p>Ao interagir com seus dados do Elasticsearch, nosso agente de IA padrão segue este fluxo padrão:</p><ol><li>Examine o prompt.</li><li>Identifique qual índice provavelmente contém as respostas para essa pergunta.</li><li>Gere uma consulta para esse índice, com base no prompt.</li><li>Pesquise esse índice com essa consulta.</li><li>Sintetize os resultados.</li><li>Os resultados respondem à pergunta? Em caso afirmativo, responda. Caso contrário, repita, mas tente algo diferente.</li></ol><p>Isso não deve parecer muito inovador - é apenas Geração Aumentada por Recuperação (RAG). E, como seria de esperar, a qualidade das suas respostas depende muito da relevância dos resultados da sua pesquisa inicial. Enquanto trabalhávamos para melhorar a qualidade de nossas respostas, prestamos muita atenção às consultas que gerávamos na etapa 3 e executávamos na etapa 4. E percebemos um padrão interessante.</p><p>Muitas vezes, quando nossas primeiras respostas eram &quot;ruins&quot;, não era porque tínhamos executado uma consulta ruim. Isso aconteceu porque <em>tínhamos escolhido o índice errado</em> para consultar. Os passos 3 e 4 geralmente não eram o nosso problema - era o passo 2.</p><h2>O que estávamos fazendo?</h2><p>Nossa implementação inicial foi simples. Tínhamos criado uma ferramenta (chamada index_explorer) que efetivamente faria um <code>_cat/indices</code> para listar todos os índices disponíveis para nós e, em seguida, pediria ao LLM para identificar qual desses índices era a melhor correspondência para a mensagem/pergunta/solicitação do usuário. Você pode ver a <a href="https://github.com/elastic/kibana/blob/0cc78184957fcd12110dabae50353392ea937508/x-pack/platform/packages/shared/onechat/onechat-genai-utils/tools/index_explorer.ts#L98-L113">implementação original aqui</a>.</p><pre><code>You are an AI assistant for the Elasticsearch company.
based on a natural language query from the user, your task is to select up to ${limit} most relevant indices from a list of indices.

*The natural language query is:* ${nlQuery}

*List of indices:*
${indices.map((index) => `- ${index.index}`).join('\n')}

Based on those information, please return most relevant indices with your reasoning.
Remember, you should select at maximum ${limit} indices.</code></pre><p>Quão bem isso estava funcionando? Não tínhamos certeza! Tínhamos exemplos claros de situações em que <em>não estava</em> funcionando bem, mas nosso primeiro desafio real foi quantificar nossa situação atual.</p><h2>Estabelecer uma linha de base</h2><h3>Tudo começa com dados.</h3><p>O que precisávamos era de um Conjunto de Dados Ideal para medir a eficácia de uma ferramenta na seleção do índice correto, dada uma solicitação do usuário e um conjunto preexistente de índices. E nós não tínhamos um conjunto de dados desse tipo disponível. Então, nós geramos um.</p><p>Reconhecimento: Sabemos que isso não é a &quot;melhor prática&quot;. Mas, às vezes, é melhor seguir em frente do que ficar discutindo detalhes irrelevantes. <a href="https://www.elastic.co/about/our-source-code#progress-perfection">Progresso, SIMPLES Perfeição</a>.</p><p>Geramos índices iniciais para vários domínios diferentes usando <a href="https://gist.github.com/seanstory/a08db2e149897da656db3a1ca72e17ac">este prompt</a>. Em seguida, para cada domínio gerado, geramos mais alguns índices usando<a href="https://gist.github.com/seanstory/a280a85d067e61bfeb5911bf2654e6e2"> esse prompt</a> (o objetivo aqui é semear confusão para o LLM com negativos difíceis e exemplos difíceis de classificar). Em seguida, editamos manualmente cada índice gerado e suas respectivas descrições. Por fim, geramos consultas de teste usando <a href="https://gist.github.com/seanstory/44291b666c05a383136f6e36bb9106fa">esse prompt</a>. Isso nos deixou com dados de exemplo como:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d96d87e286eddbc012402b1ecccd57419a99253-1600x782.png" style="width: 100%;" /><p>e casos de teste como:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4c2e9ad941d98d7e66033bbc08c9b8060ec19097-1600x797.png" style="width: 100%;" /><h3>Construindo um arnês de teste</h3><p>A partir daqui, o processo foi muito simples. Crie uma ferramenta que possa:</p><ol><li>Crie um ambiente totalmente novo com um cluster Elasticsearch de destino.</li><li>Crie todos os índices definidos no conjunto de dados de destino.</li><li>Para cada cenário de teste, execute a ferramenta i<code>ndex_explorer</code> (felizmente, temos uma <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-agent-builder-tools-execute">API Execute Tool</a>).</li><li>Compare o índice resultante com o índice esperado e registre o resultado.</li><li>Após concluir todos os cenários de teste, tabule os resultados.</li></ol><h3>A pesquisa indica…</h3><p>Os resultados iniciais foram, previsivelmente, medíocres.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9c10679bcd6291edfa2a9ba42e7dd922aa483f0b-1216x806.png" style="width: 100%;" /><p>No geral, a precisão na identificação do índice correto foi de 77,14%. E isso no cenário &quot;ideal&quot;, onde todos os índices têm nomes bons e semanticamente significativos. Qualquer pessoa que já tenha executado um `PUT test2/_doc/foo {...}` sabe que seus índices nem sempre têm nomes significativos.</p><p>Portanto, temos uma base de referência, e ela mostra que há muito espaço para melhorias. Chegou a hora de fazer ciência! 🧪</p><h2>Experimentação</h2><h3>Hipótese 1: Os mapeamentos ajudarão</h3><p>O objetivo aqui é identificar um índice que contenha dados relevantes para a pergunta original. E a parte de um índice que melhor descreve os dados que ele contém são os <em>mapeamentos</em> do índice. Mesmo sem obter nenhuma amostra do conteúdo do índice, saber que o índice possui um campo de preço do tipo double implica que os dados representam algo que está à venda. Um campo de autor do tipo texto implica alguns dados linguísticos não estruturados. A combinação dos dois pode sugerir que os dados são livros/histórias/poemas. Podemos obter muitas pistas semânticas apenas conhecendo as propriedades de um índice. Então, em uma branch local, eu ajustei nosso arquivo `.index_explorer`. Ferramenta para enviar os mapeamentos completos de um índice (juntamente com seu nome) ao LLM para que este tome uma decisão. </p><p>O resultado (dos registros do Kibana):</p><pre><code>[2025-09-05T11:01:21.552-05:00][ERROR][plugins.onechat] Error: Error calling connector: event: error
data: {"error":{"code":"request_entity_too_large","message":"Received a content too large status code for request from inference entity id [.rainbow-sprinkles-elastic] status [413]","type":"error"}}


    at createInferenceProviderError (errors.ts:90:10)
    at convertUpstreamError (convert_upstream_error.ts:39:38)
    at handle_connector_response.ts:26:33
    at Observable.init [as _subscribe] (/Users/seanstory/Desktop/Dev/kibana/node_modules/rxjs/src/internal/observable/throwError.ts:123:68)...</code></pre><p>Os autores originais da ferramenta já haviam previsto isso. Embora o mapeamento de um índice seja uma mina de ouro de informações, ele também é um bloco JSON bastante extenso. E em um cenário realista onde você está comparando inúmeros índices (nosso conjunto de dados de avaliação define 20), esses blocos JSON se acumulam. Assim, queremos fornecer ao LLM mais contexto para sua decisão, não apenas os nomes dos índices de todas as opções, mas também os mapeamentos completos de cada uma.</p><h3>Hipótese 2: Mapeamentos “achatados” (listas de campos) como solução de compromisso.</h3><p>Partimos do pressuposto de que os criadores de índices usarão nomes de índice semanticamente significativos. E se estendermos essa suposição também aos nomes dos campos? Nosso experimento anterior falhou porque o mapeamento de JSON inclui MUITOS metadados e código repetitivo desnecessários.</p><pre><code>     "description_text": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "description_semantic"
          ]
        },</code></pre><p>O bloco acima, por exemplo, tem 236 caracteres e define apenas um único campo em um mapeamento do Elasticsearch. Enquanto a string “description_text” possui apenas 16 caracteres. Isso representa um aumento de quase 15 vezes na contagem de caracteres, sem uma melhoria semântica significativa na descrição do que esse campo implica sobre os dados disponíveis. E se buscássemos os mapeamentos para todos os índices, mas antes de enviá-los para o LLM, os &quot;aplanássemos&quot; em uma lista contendo apenas os nomes de seus campos?</p><p>Nós experimentamos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/112c2f447c11f154b5082725cd49b51d0a3c8a65-1214x804.png" style="width: 100%;" /><p>Isso é ótimo! Melhorias em todos os aspectos. Mas será que poderíamos fazer melhor?</p><h3>Hipótese 3: Descrições no mapeamento _meta</h3><p>Se apenas os nomes dos campos, sem nenhum contexto adicional, causaram um salto tão grande, presumivelmente adicionar um contexto substancial seria ainda melhor! Não é necessariamente convencional que cada índice tenha uma descrição associada, mas é possível adicionar metadados de qualquer tipo ao objeto _meta do mapeamento. Retornamos aos índices gerados e adicionamos descrições para cada índice em nosso conjunto de dados. Contanto que as descrições não sejam excessivamente longas, elas devem usar menos tokens do que o mapeamento completo e fornecer informações significativamente melhores sobre quais dados estão incluídos no índice. Nosso experimento validou essa hipótese.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/32d2692ad4479d0e52d8ee723dcc5710a6ec90f3-1208x806.png" style="width: 100%;" /><p>Uma pequena melhoria, e agora temos mais de 90% de precisão em todos os aspectos.</p><h3>Hipótese 4: O todo é maior que a soma das partes.</h3><p>Os nomes dos campos aumentaram nossos resultados. As descrições aumentaram nossos resultados. Portanto, utilizar <em>tanto </em>as descrições quanto os nomes dos campos deve apresentar resultados ainda melhores, certo?</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/114cbb408ff16b136251d2265416bd5270380fe5-1208x794.png" style="width: 100%;" /><p>Os dados indicaram &quot;não&quot; (nenhuma mudança em relação ao experimento anterior). A principal teoria era que, como as descrições foram geradas a partir dos campos/mapeamentos do índice, não havia informações suficientes entre esses dois contextos para adicionar algo &quot;novo&quot; ao combiná-los. Além disso, a carga útil que estamos enviando para nossos 20 índices de teste está ficando bastante grande. A linha de raciocínio que seguimos até agora não é escalável. Na verdade, há bons motivos para acreditar que nenhum dos nossos experimentos até agora funcionaria em clusters Elasticsearch, onde existem centenas ou milhares de índices para escolher. Qualquer abordagem que aumente linearmente o tamanho da mensagem enviada ao LLM à medida que o número total de índices aumenta provavelmente não será uma estratégia generalizável.</p><p>O que realmente precisamos é de uma abordagem que nos ajude a reduzir um grande número de candidatos apenas às opções mais relevantes…</p><p>O que temos aqui é um problema de busca.</p><h3>Hipótese 5: Seleção via busca semântica</h3><p>Se o nome de um índice tiver significado semântico, ele poderá ser armazenado como um vetor e pesquisado semanticamente.</p><p>Se os nomes dos campos de um índice tiverem significado semântico, eles podem ser armazenados como vetores e pesquisados semanticamente.</p><p>Se um índice possui uma descrição com significado semântico, ele também pode ser armazenado como um vetor e pesquisado semanticamente.</p><p>Atualmente, os índices do Elasticsearch não tornam nenhuma dessas informações pesquisável (talvez devêssemos!), mas foi bastante trivial<a href="https://github.com/elastic/connectors/pull/3638"> improvisar algo</a> que pudesse contornar essa lacuna. Utilizando a estrutura de conectores da Elastic, criei um conector que gera um documento para cada índice em um cluster. Os documentos resultantes seriam algo como:</p><pre><code> doc = {
                "_id": index_name,
                "index_name": index_name,
			"meta_description”: description,
"field_descriptions" = field_descriptions,
                "mapping": json.dumps(mapping),  
                "source_cluster": self.es_client.configured_host,
            }</code></pre><p>Enviei esses documentos para um novo índice onde defini manualmente o mapeamento da seguinte forma:</p><pre><code>{
   "mappings": {
       "properties": {
           "semantic_content": {
               "type": "semantic_text"
           },
           "index_name": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "mapping": {
               "type": "keyword",
               "copy_to": "semantic_content"
           },
           "source_cluster": {
               "type": "keyword"
           },
           "meta_description": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "field_descriptions": {
               "type": "text",
               "copy_to": "semantic_content"
           }
       }
   }
}</code></pre><p>Isso cria um único campo semantic_content, onde todos os outros campos com significado semântico são divididos em blocos e indexados. A busca neste índice torna-se trivial, bastando:</p><pre><code>GET indexed-indices/_search
{
 "query": {
   "semantic": {
     "field": "semantic_content",
     "query": "$query"
   }
 }
}</code></pre><p>A ferramenta <code>index_explorer</code> modificada agora é <em>muito</em> mais rápida, pois não precisa fazer uma solicitação a um LLM, mas pode solicitar um único embedding para a consulta fornecida e executar uma operação de busca vetorial eficiente. Considerando o resultado mais relevante como nosso índice selecionado, obtivemos os seguintes resultados:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/06ef5d78040d064d3444793f636d527d9e19a869-1214x800.png" style="width: 100%;" /><p>Essa abordagem é escalável. Essa abordagem é eficiente. Mas essa abordagem é pouco melhor do que a nossa abordagem inicial. Isso não é surpreendente; a abordagem de busca aqui é incrivelmente ingênua. Não há nuances. Não há reconhecimento de que o nome e a descrição de um índice devam ter mais peso do que um nome de campo arbitrário que o índice contenha. Não há como priorizar correspondências lexicais exatas em detrimento de correspondências sinônimas. No entanto, construir uma consulta altamente detalhada exigiria muitas suposições sobre os dados disponíveis. Até agora, já fizemos algumas suposições importantes sobre o significado semântico dos nomes de índices e campos, mas precisaríamos ir um passo além e começar a supor <em>o quanto</em> de significado eles têm e como se relacionam entre si. Sem fazer isso, provavelmente não conseguiremos identificar com segurança a melhor correspondência como nosso resultado principal, mas podemos afirmar com mais certeza que a melhor correspondência está em algum lugar entre os N melhores resultados. Precisamos de algo que possa consumir informações semânticas no contexto em que existem, comparando-as com outra entidade que pode se representar de uma maneira semanticamente distinta, e fazendo um julgamento entre elas. Como um mestrado em Direito.</p><h3>Hipótese 6: Redução do conjunto de candidatos</h3><p>Houve vários outros experimentos que vou abordar superficialmente, mas o principal avanço foi abandonar a ideia de escolher a melhor correspondência puramente com base em uma busca semântica e, em vez disso, usar a busca semântica como um filtro para eliminar índices irrelevantes da análise do LLM. Combinamos os algoritmos Linear Retrievers, Hybrid Search com RRF e <code>semantic_text</code> em <a href="https://gist.github.com/seanstory/d704443120e20f6c844db10e30066860">nossa busca</a>, limitando os resultados aos 5 índices de correspondência principais.</p><p>Em seguida, para cada correspondência, adicionamos o nome do índice, a descrição e os nomes dos campos a uma mensagem para o LLM. Os resultados foram fantásticos:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8fcabd78f591f90d6bc7c0e087d31317e4eef791-1206x804.png" style="width: 100%;" /><p>A maior precisão obtida em qualquer experimento até hoje! E como essa abordagem não aumenta o tamanho da mensagem proporcionalmente ao número total de índices, ela é muito mais escalável.</p><h2>Resultados</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/04d630797213dbb8bf567da41d1cdd5c7b4586c9-1600x521.png" style="width: 100%;" /><p>O primeiro resultado claro foi que nossa linha de base <em>pode</em> ser melhorada. Isso parece óbvio em retrospectiva, mas antes do início da experimentação, houve uma discussão séria sobre se deveríamos abandonar completamente nossa ferramenta <code>index_explorer</code> e confiar na configuração explícita do usuário para limitar o espaço de busca. Embora essa ainda seja uma opção viável e válida, esta pesquisa mostra que existem caminhos promissores para automatizar a seleção de índices quando essas informações fornecidas pelo usuário não estão disponíveis.</p><p>A próxima conclusão clara foi que simplesmente adicionar mais caracteres descritivos ao problema tem resultados cada vez menores. Antes desta pesquisa, estávamos debatendo se deveríamos investir na expansão da capacidade do Elasticsearch para armazenar <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-field-meta">metadados em nível de campo</a>. Atualmente, esses valores <code>meta</code> são limitados a 50 caracteres, e havia uma suposição de que precisaríamos aumentar esse valor para podermos obter uma compreensão semântica de nossos campos. Claramente, esse não é o caso, e o LLM parece funcionar muito bem apenas com os nomes das áreas de estudo. Poderemos investigar isso mais a fundo posteriormente, mas já não parece urgente.</p><p>Por outro lado, isso forneceu evidências claras da importância de se ter metadados de índice &quot;pesquisáveis&quot;. Para esses experimentos, nós hackeamos um índice de índices. Mas isso é algo que poderíamos investigar, integrando diretamente ao Elasticsearch, criando APIs para gerenciar ou, pelo menos, estabelecendo uma convenção a respeito. Estaremos avaliando nossas opções e discutindo internamente, então fiquem atentos.</p><p>Finalmente, esse esforço confirmou o valor de dedicarmos tempo para experimentar e tomar decisões baseadas em dados. Na verdade, isso nos ajudou a reafirmar que nosso produto Agent Builder precisará de recursos robustos de avaliação integrados. Se precisarmos construir toda uma estrutura de testes apenas para uma ferramenta que seleciona índices, nossos clientes certamente precisarão de maneiras de avaliar qualitativamente suas ferramentas personalizadas à medida que fazem ajustes iterativos.</p><p>Estou ansioso para ver o que vamos construir, e espero que você também esteja!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como implantar o Elasticsearch no AWS Marketplace]]></title>
            <description><![CDATA[Aprenda como configurar e executar o Elasticsearch usando o Elastic Cloud Service no AWS Marketplace neste guia passo a passo.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/aws-elasticsearch-service-set-up</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/aws-elasticsearch-service-set-up</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Fri, 03 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a80776d2ef85db26f850d932339fac2d26b90278-1086x620.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, vamos aprender como implantar o Elasticsearch na AWS usando as ofertas do Marketplace.</p><p>Vamos usar o Elastic Cloud Service na AWS, que é o serviço oficial gerenciado do Elasticsearch Service que simplifica a implantação e orquestração de todos os componentes do Elastic Stack por meio da infraestrutura nativa da AWS.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f15814051b53b50bec38f9a9f515a1e6dc08a56c-884x440.png" style="width: 100%;" /><p>Se você quer aprender a instalar e configurar o Elasticsearch na AWS EC2, leia <a href="https://www.elastic.co/search-labs/blog/elasticsearch-on-aws-ec2-deployment-guide">este blog</a>.<br/></p><h2>O que é o AWS Marketplace?</h2><p><a href="https://aws.amazon.com/marketplace"><strong>Elastic on AWS Marketplace</strong></a> oferece uma experiência de busca e análise totalmente gerenciada, em que a AWS cuida da provisão de infraestrutura, segurança e redimensionamento, enquanto os desenvolvedores se concentram na criação de aplicações de busca. Isso permite que as equipes implantem clusters do Elasticsearch de nível empresarial em minutos com integrações integradas da AWS.</p><h2>Quando usar o Elastic no AWS Marketplace?</h2><p>O Elastic on AWS Marketplace é mais indicado para organizações com infraestrutura AWS existente que buscam implantar o Elasticsearch com serviços gerenciados, security integrado e integrações AWS integradas sem sobrecarga operacional.</p><h2>Como configurar o Elastic Cloud no AWS Marketplace</h2><h3>Passo 1: Acesse o AWS Marketplace</h3><p>1. Faça login no <a href="https://console.aws.com/">AWS</a></p><ul><li>Na barra de busca, procure por AWS Marketplace</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc9928f79482c2c01e33978c88d390a2bfa2a3bf-1600x340.png" style="width: 100%;" /><p>2. No painel de navegação à esquerda, clique em <strong>Descobrir produtos</strong> e depois busque por Elasticsearch</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ca4232271cb13ebfe33de406ecaec085033ec8a0-1454x760.png" style="width: 100%;" /><p>3. Clique em <strong>Elastic Cloud (Elasticsearch Service)</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e59aa276e55532f2ac3461d0ca983af4d41ad7a6-1600x611.png" style="width: 100%;" /><h3>Passo 2: Assine o serviço</h3><p>1. Selecione <strong>opções de compra</strong> ou clique em <strong>Experimente gratuitamente</strong></p><p>2. <strong>Revise os detalhes de preços</strong>, <strong>termos e condições</strong> e <strong>detalhes de compra</strong></p><p>3. Clique no botão <strong>Assinar </strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c30d12b6b1061e76771d518011e522285f939f1-1600x290.png" style="width: 100%;" /><p>4. Agora precisamos configurar a conta Elastic. Siga os passos da AWS</p><p>a. Clique no botão Ativar integração</p><p>b. Clique no botão Entrar ou crie uma conta de fornecedor</p><p>c. Clique no botão Executar modelo</p><p>d. Clique no botão Executar software</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/879d9f0f01406e1955e1b38a2f6f2192ef040344-852x722.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fc59be78cf776aa12867f40810598419576cbd39-1600x1143.png" style="width: 100%;" /><h3>Passo 3. Configure sua nova conta na Elastic</h3><p>1. Crie sua conta Elastic</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5efeaf0737062a55470b17b67651f220e12183f2-986x905.png" style="width: 100%;" /><p>2. Valide seu endereço de e-mail</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e0cfaac278614e317ce278935040bfa5a58edd13-853x894.png" style="width: 100%;" /><p>3. Insira seu nome e informações da empresa</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5658fe66c3b1bcced73e822eae006846f0ddd9e-997x903.png" style="width: 100%;" /><p>4. Complete uma breve pesquisa da Elastic</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a3f53c00dadfd22f7d739a920c87d5f387182833-892x805.png" style="width: 100%;" /><p>5. Selecione a região onde deseja hospedar o Elastic Cloud. Por padrão, sua região AWS real será selecionada</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1dcdf3bf91c305821daaa25a60aa03be6454c1c-1207x1032.png" style="width: 100%;" /><p>6. Aguarde o Elastic implantar.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/50903e57ebea7cc47bdfabf4750b4ba2a7a91148-1370x1266.png" style="width: 100%;" /><p>7. Sua implantação está conectada à sua assinatura do AWS Marketplace.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3bb731e2d5de5053ccecb77e45dbdbcdaf294dba-1600x1288.png" style="width: 100%;" /><h2>Cancele sua assinatura</h2><p>Para cancelar a sua assinatura</p><p>1. Acesse o <a href="https://console.aws.com/">console da AWS</a></p><p>Busque por AWS Marketplace na barra de pesquisa. Clique em <strong>AWS Marketplace</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aeed3d1e67b4cef91934de257a6fd6daa9737a12-1600x554.png" style="width: 100%;" /><p>2. Clique em <strong>Assinatura do Elastic Cloud</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/04e6cc41850226df223dbe2d1b0e4b45265f6c39-1600x564.png" style="width: 100%;" /><p>3. Clique no botão <strong>Ações</strong> e, em seguida, clique em <strong>Cancelar assinatura</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e33d334ea6541c637a223de3ebd6209def75a6d3-1600x1039.png" style="width: 100%;" /><p>4. Confirme o cancelamento e clique em <strong>Sim </strong>e no botão<strong> cancelar assinatura</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b73b3187168adc7aefdd46f95be33c1bce3da1e4-1103x698.png" style="width: 100%;" /><p>5. Uma mensagem de confirmação aparecerá no topo da página.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c5ba66a23d535e866a8b458e5aca82c5f0b93037-1600x639.png" style="width: 100%;" /><h2>Próximas etapas</h2><p>Comece sua jornada no Elastic Cloud com um teste gratuito de 7 dias que inclui uma única implantação e três projetos<a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k"> Elastic Cloud (Elasticsearch Service)</a>. Basta fazer login na sua conta AWS e clicar em &quot;Ver Opções de Compra&quot; para começar a usar a Plataforma de IA de Busca da Elastic imediatamente no Elastic<a href="https://aws.amazon.com/marketplace/pp/prodview-voru33wi6xs7k"> Cloud (Elasticsearch Service)</a>. O teste oferece acesso total a soluções de busca, segurança e observabilidade, sem qualquer sobrecarga de gerenciamento de infraestrutura.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AutoOps: Uma jornada para simplificar o gerenciamento autogerenciado do Elasticsearch.]]></title>
            <description><![CDATA[Vamos explorar o AutoOps para Elasticsearch autogerenciado (ambientes no local ou hospedados privadamente). Mostraremos o valor, como configurá-lo e os insights que ele oferece.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elastic-autoops-self-managed-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elastic-autoops-self-managed-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Ori Shafir]]></dc:creator>
            <pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4538ca4c6cdde99d88f1cead7515716fac217b92-1600x883.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Apresentamos o AutoOps para Elasticsearch autogerenciado (ambientes locais ou hospedados de forma privada), que facilita o gerenciamento do Elasticsearch. Em vez de uma <em>explicação técnica tradicional de recurso</em>, este blog destaca o valor do AutoOps, como configurá-lo e os insights que ele oferece, sob a perspectiva de um engenheiro DevOps, porque o verdadeiro valor do AutoOps é melhor percebido no trabalho diário de gerenciamento do Elasticsearch em grande escala.</p><h2>Capítulo 1: Contexto - A complexidade por trás da autogestão em larga escala</h2><p><strong>Operar qualquer plataforma de dados autogerenciada em grande escala pode ser complexo.</strong></p><p>Num instante, as consultas são extremamente rápidas. Em seguida, ocorrem atrasos na ingestão e os custos de armazenamento disparam. É basicamente como administrar um zoológico, só que os animais podem te chamar às 3 da manhã.</p><p>Meu ambiente não é diferente: múltiplos clusters, buscas complexas entre clusters (CCS) e centenas de usuários em diversos departamentos.</p><p>Utilizamos o Stack Monitoring para as operações diárias. Fornece gráficos e métricas, mas ainda exige muita experiência e tempo para conectar os pontos. Diagnosticar gargalos ou saber quando ajustar as estratégias de fragmentação ainda é um processo manual e sujeito a erros. Em muitos casos, os problemas passam despercebidos até causarem uma interrupção, uma queda de desempenho ou um pico inesperado de armazenamento.</p><h2>Capítulo 2: Descobrindo o AutoOps</h2><p>Em seguida, veio o anúncio: <strong><a href="https://www.elastic.co/platform/autoops">o AutoOps</a> agora está disponível para clusters autogerenciados – em ambientes locais ou hospedados de forma privada.</strong></p><p>Há muito tempo que o AutoOps ajuda os usuários da Elastic Cloud a gerenciar implantações com mais eficiência. Agora, esses mesmos benefícios estão disponíveis para clusters autogerenciados (ECK, ECE ou autônomos) executados no local ou em ambientes de nuvem privada, sem nenhum custo, independentemente do seu nível de licença.</p><p>A proposta do AutoOps é tentadora:</p><ul><li>Detecção de problemas em tempo real, como gargalos de ingestão, shards desbalanceados, consultas lentas e muito mais.</li><li>Recomendações práticas personalizadas para a configuração do seu cluster.</li><li>Informações sobre otimização de recursos para melhorar a eficiência e reduzir gastos desnecessários.</li><li>Configuração simples com a instalação de um agente leve — sem necessidade de infraestrutura adicional.</li></ul><p>Sinceramente, qualquer coisa que prometesse &quot;nenhuma infraestrutura extra&quot; chamava totalmente a minha atenção.</p><h2>Capítulo 3: Configuração em 5 minutos (sim, é verdade)</h2><p>Reservei minha tarde, comprei bastante café e me preparei para uma longa montagem. Para minha surpresa, levou apenas cinco minutos:</p><ol><li>Acessei minha <a href="https://cloud.elastic.co/registration?onboarding_service_type=ccm-autoops">conta do Elastic Cloud.</a></li><li>Decidi como executar os agentes: Elastic Cloud Kubernetes (ECK), Kubernetes, Docker ou Linux.</li><li>Inseri o URL do cluster</li><li>Recebi um único comando para executar, que instalou um agente Metricbeat leve.</li></ol><p>É isso. Meu cluster estava conectado.</p><p>Não é necessário provisionar clusters de monitoramento dedicados. E, o mais importante, o AutoOps envia apenas <strong>métricas,</strong> o que significa que os dados da minha empresa ficam armazenados no meu ambiente autogerenciado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/cb8e1ebc0f23a6db6b0e4300c9cdcd165623ca39-1184x734.png" style="width: 100%;" /><p><em>Passo 1: Cadastre-se no Elastic Cloud</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b208830db4d74feef700473ce07b052e0c72f48b-3446x1716.png" style="width: 100%;" /><p><em>Etapa 2: Escolha onde executar o Agente</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/850d8c0d6b9e018408969aa05b67dbd865a26d30-1184x734.png" style="width: 100%;" /><p><em>Passo 3: Insira seu endpoint do Elasticsearch e como autenticar.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a472889a277caabb9ecd3fb9cd57d3b3eac0100-1184x734.png" style="width: 100%;" /><p><em>Passo 4: Comando simples para instalar o Agente</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f9a7283469a321a9c472d655cea11d9d0ea7b1e-1600x883.png" style="width: 100%;" /><p><em>Pronto: depois de alguns minutos, o AutoOps começará a mostrar insights.</em></p><p>Para obter mais detalhes, consulte a <a href="https://www.elastic.co/docs/deploy-manage/monitor/autoops/cc-connect-self-managed-to-autoops">documentação de integração do AutoOps</a> e as <a href="https://www.elastic.co/docs/deploy-manage/monitor/autoops/cc-cloud-connect-autoops-faq">perguntas frequentes</a>.</p><h2>Capítulo 4: primeiras percepções, primeiras vitórias</h2><p>Em poucos minutos, o AutoOps começou a revelar informações, fornecendo análises da causa raiz e etapas claras para corrigi-las.</p><p>Os destaques da primeira semana incluíram:</p><ul><li>Índices sinalizados não vinculados a nenhuma política ILM que haviam crescido demais.</li><li>Um dos clusters tinha três nós vazios, remanescentes de uma manutenção anterior.</li><li>Alguns nós estavam cruzando marcas d&#x27;água e alguns índices estavam sem réplicas.</li><li>Detectei um modelo mal configurado.</li><li>Identificou uma pesquisa de longa duração e sugeriu o comando exato para cancelá-la.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f7d16355938ffcb00127deaf833d7a4d0acdb188-1600x894.png" style="width: 100%;" /><p><em>O AutoOps detectou que o cluster está rejeitando a indexação.</em></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9d6372bc489e9954e6f78d81047d7a25feef1703-1600x894.png" style="width: 100%;" /><p><em>O AutoOps detectou que alguns índices estão configurados sem uma réplica.</em><br/>Antes do AutoOps, teríamos investido em mais hardware para resolver esses problemas. Em vez disso, o AutoOps apontou diretamente para a causa raiz, e as correções levaram minutos.</p><p>Desta vez, um sistema de monitoramento não estava apenas me mostrando gráficos, mas me dizendo como resolver o problema. Comecei a me perguntar se o AutoOps também poderia me ajudar a diagnosticar minha rede Wi-Fi doméstica e finalmente me libertar da função de departamento de TI da minha família…</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eac09f1d730955453cd79900002b100bc741ad97-1600x894.png" style="width: 100%;" /><p><em>O AutoOps monitorava o tamanho dos shards e emitia alertas quando havia muitos shards vazios.</em></p><h2>Capítulo 5: apoio que vê o que eu vejo</h2><p>Na primeira vez que abri um chamado de suporte, percebi outra vantagem: os engenheiros de suporte da Elastic podiam ver exatamente os mesmos dados e recomendações que eu estava analisando.</p><p>Transformou o apoio em colaboração. Em vez de ficar trocando tickets sem parar, a sensação era de estar trabalhando com um colega que conhece o Elasticsearch como a palma da mão.</p><h2>Capítulo 6: Operando em escala</h2><p>Antes do AutoOps, escalar o Elasticsearch parecia uma mistura de ciência, instinto e conhecimento tácito.</p><p>Agora é orientado por dados, com visibilidade clara e recomendações:</p><ul><li>Visibilidade da utilização de recursos para evitar o provisionamento excessivo.</li><li>Alocação de shards mais inteligente e recomendações de hierarquização para um desempenho equilibrado.</li><li>Informações sobre dimensionamento de índices que reduzem o desperdício de armazenamento e custos de hardware.</li><li>Análise de causa raiz mais rápida em vários clusters</li></ul><h2>Capítulo 7: o primeiro de muitos serviços conectados à nuvem</h2><p>O AutoOps é mais do que uma ferramenta independente. É o primeiro de um novo conjunto de <a href="https://www.elastic.co/docs/deploy-manage/cloud-connect"><strong>Serviços Conectados à Nuvem</strong></a> para clientes com gestão própria. O Cloud Connect permite que clusters com gestão própria consumam serviços do Elastic Cloud sem a sobrecarga operacional de instalar e gerenciar esses serviços em seu próprio ambiente. Os recursos são implementados automaticamente, permitindo que as equipes recebam melhorias mais rapidamente e com menor complexidade de infraestrutura.</p><p>A seguir: Elastic Inference Service (EIS).</p><h2>Considerações finais</h2><p>Gerenciar implantações autogerenciadas em larga escala não precisa ser algo avassalador.</p><p>E se você preferir operações ainda mais simples, sempre poderá migrar algumas cargas de trabalho para o <a href="https://cloud.elastic.co/registration?pg=global&amp;plcmt=nav&amp;cta=205352-primary">Elastic Cloud</a>, seja na versão hospedada ou sem servidor, para executar o Elasticsearch da maneira mais fácil.</p><p>Se você deseja continuar executando o gerenciamento próprio, conecte qualquer cluster autogerenciado ao <a href="https://cloud.elastic.co/registration?onboarding_service_type=ccm-autoops">AutoOps no Elastic Cloud,</a> ele é gratuito para todas as licenças Elastic.</p><h2>Resumindo</h2><p>Gerenciar grandes clusters Elasticsearch autogerenciados é complexo e demorado. O AutoOps oferece detecção de problemas em tempo real, recomendações práticas e visibilidade compartilhada com o Elastic Support, sem a necessidade de gerenciar infraestrutura adicional. A configuração leva apenas alguns minutos e as informações aparecem imediatamente.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Seu primeiro Agente Elástico: De uma simples consulta a um chat com inteligência artificial.]]></title>
            <description><![CDATA[Aprenda a usar o construtor de agentes de IA da Elastic para criar agentes de IA especializados. Neste blog, vamos construir um agente de IA para o setor financeiro.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-agent-builder-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-agent-builder-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
            <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Com o novo <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder</a> da Elastic, você pode criar agentes de IA especializados que atuam como especialistas em seus domínios de negócios específicos. Essa funcionalidade vai além de simples painéis e barras de pesquisa, transformando seus dados de um recurso passivo em um parceiro ativo e interativo.</p><p>Imagine um gerente financeiro que precisa se atualizar antes de uma reunião com um cliente. Em vez de vasculhar manualmente os feeds de notícias e comparar painéis de portfólio, agora eles podem simplesmente fazer uma pergunta direta ao seu agente personalizado. Essa é a vantagem de uma abordagem que prioriza o bate-papo. O gestor tem acesso direto e conversacional aos seus dados, podendo fazer perguntas como: &quot;Quais são as últimas notícias sobre a ACME Corp e como isso afeta os investimentos do meu cliente?&quot; e obtendo uma resposta sintetizada e especializada em segundos.</p><p>Embora estejamos criando um especialista financeiro hoje, as aplicações são tão variadas quanto seus dados. O mesmo poder pode criar um analista de cibersegurança para procurar ameaças, um engenheiro de confiabilidade de sites para diagnosticar uma interrupção ou um gerente de marketing para otimizar uma campanha. Independentemente da área, a missão principal é a mesma: transformar seus dados em um especialista com quem você possa conversar.</p><h2>Etapa 0: Nosso conjunto de dados</h2><p>Nosso conjunto de dados hoje é um conjunto de dados sintético baseado em finanças, composto por contas financeiras, posições de ativos, notícias e relatórios financeiros. Embora sintética, ela replica uma versão simplificada de um conjunto de dados financeiro real.</p><p><code>financial_accounts</code>Portfólios de clientes com perfis de risco</p><p><code>financial_holdings</code>Posições em ações/ETFs/títulos com histórico de compras</p><p><code>financial_asset_details</code>Detalhes sobre a ação/ETF/título</p><p><code>financial_news</code>Artigos de mercado gerados por IA com análise de sentimento</p><p><code>financial_reports</code>Resultados da empresa e notas dos analistas</p><p>Você pode carregar este conjunto de dados por conta própria seguindo as instruções do notebook que acompanha este documento, localizado <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">aqui</a>.</p><h2>Etapa 1: A Base — Sua Lógica de Negócios em ES|QL</h2><p>Toda habilidade de IA começa com uma base lógica sólida. Para o nosso agente de Gestão Financeira, precisamos ensiná-lo a responder a uma pergunta comum: &quot;Estou preocupado com o sentimento do mercado.&quot; Você pode me mostrar quais dos nossos clientes correm maior risco em caso de más notícias? Essa questão vai além de uma simples pesquisa. Isso exige que correlacionemos o sentimento do mercado com as carteiras dos clientes.</p><p>Precisamos encontrar os ativos mencionados em artigos negativos, identificar todos os clientes que possuem esses ativos, calcular o valor de mercado atual da sua exposição e, em seguida, classificar os resultados para priorizar o maior risco. Essa análise complexa de múltiplas junções é a tarefa perfeita para nossa ferramenta avançada ES|QL.</p><p>Aqui está a consulta completa que usaremos. Parece impressionante, mas os conceitos são simples.</p><pre><code>FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) >= NOW() - TO_TIMEDURATION(?time_duration)
        | RENAME primary_symbol AS symbol
        | LOOKUP JOIN financial_asset_details ON symbol
        | LOOKUP JOIN financial_holdings ON symbol
        | LOOKUP JOIN financial_accounts ON account_id
        | WHERE account_holder_name IS NOT NULL
        | EVAL position_current_value = quantity * current_price.price
        | RENAME title AS news_title
        | KEEP
            account_holder_name, symbol, asset_name, news_title,
            sentiment, position_current_value, quantity, current_price.price,
            published_date, report_date
        | SORT position_current_value DESC
        | LIMIT 50</code></pre><h2>Analisando em detalhes: Junções e guarda-corpos</h2><p>Nesta consulta, dois conceitos importantes entram em jogo e são essenciais para a criação do Agent Builder.</p><h3>1. A junção de pesquisa</h3><p>Durante anos, uma das funcionalidades mais solicitadas no Elasticsearch tem sido a capacidade de unir dados de diferentes índices com base em uma chave comum. Com ES|QL, isso agora é possível com <code>LOOKUP JOIN</code>.</p><p>Em nossa nova consulta, realizamos uma cadeia de três <code>LOOKUP JOIN</code>: primeiro conectando notícias negativas aos detalhes dos ativos, depois vinculando esses ativos às participações do cliente e, finalmente, unindo às informações da conta do cliente. Isso gera um resultado incrivelmente rico a partir de quatro índices diferentes em uma única consulta eficiente. Isso significa que podemos combinar conjuntos de dados distintos para criar uma resposta única e esclarecedora sem precisar desnormalizar todos os nossos dados em um único índice gigante antecipadamente.</p><h3>2. Parâmetros como guarda-corpos LLM</h3><p>Você notará que a consulta usa <code>?time_duration</code>. Isso não é apenas uma variável; é uma proteção para a IA. Embora os Modelos de Linguagem de Grande Porte (LLMs, na sigla em inglês) sejam ótimos para gerar consultas, permitir que eles tenham livre acesso aos seus dados pode levar a consultas ineficientes ou até mesmo incorretas.</p><p>Ao criar uma consulta parametrizada, forçamos o LLM a funcionar dentro da lógica de negócios testada, eficiente e correta que um especialista humano já definiu. É semelhante à forma como os desenvolvedores usam modelos de pesquisa há anos para expor com segurança os recursos de consulta aos aplicativos. O agente pode interpretar a solicitação de um usuário como &quot;esta semana&quot; para preencher o parâmetro <code>time_duration</code> , mas deve usar nossa estrutura de consulta para obter a resposta. Isso nos proporciona o equilíbrio perfeito entre flexibilidade e controle.</p><p>Em última análise, essa consulta permite que um especialista que entende os dados incorpore seu conhecimento em uma ferramenta. Outras pessoas — e agentes de IA — podem então usar essa ferramenta para obter resultados correlacionados, fornecendo simplesmente um único parâmetro, sem precisar saber nada sobre a complexidade subjacente.</p><h2>Etapa 2: A Habilidade — Transformar uma Consulta em uma Ferramenta Reutilizável</h2><p>Uma consulta ES|QL é apenas texto até que a registremos como uma <strong>ferramenta</strong>. No Construtor de Agentes, uma ferramenta é mais do que apenas uma consulta salva; é uma &quot;habilidade&quot; que um agente de IA pode entender e optar por usar. A mágica está na <strong>descrição em linguagem natural</strong> que fornecemos. Essa descrição serve de ponte entre a pergunta do usuário e a lógica de consulta subjacente. Vamos registrar a consulta que acabamos de criar.</p><h3>O Caminho da Interface do Usuário</h3><p>Criar uma ferramenta no Kibana é um processo simples.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" style="width: 100%;" /><p>1. Navegue até <strong>Agentes</strong></p><ul><li>Clique em<strong> Ferramentas </strong>ou <strong>Gerenciar Ferramentas</strong> e clique no botão <strong>Nova ferramenta</strong> .</li></ul><p>2. Preencha o formulário com os seguintes dados:</p><ul><li><strong>ID da ferramenta:</strong> <code>find_client_exposure_to_negative_news</code></li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu. Este é o ID exclusivo da ferramenta.</p><ul><li><strong>Descrição:</strong> &quot;Identifica a exposição da carteira de clientes a notícias negativas.&quot; Esta ferramenta analisa notícias e relatórios recentes em busca de sentimentos negativos, identifica o ativo associado e encontra todos os clientes que possuem esse ativo. Retorna uma lista ordenada pelo valor de mercado atual da posição para destacar o maior risco potencial.&quot;</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu. É isso que o LLM lê para decidir se essa ferramenta é a adequada para o trabalho.</p><ul><li><strong>Rótulos</strong>: <code>retrieval</code> e <code>risk-analysis</code></li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Etiquetas são usadas para ajudar a agrupar várias ferramentas.</p><ul><li><strong>Configuração:</strong> Cole a consulta ES|QL completa da Etapa 1.</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu. Esta é a pesquisa que o agente usará.</p><p>3. Clique em <strong>Inferir parâmetros da consulta</strong>. A interface do usuário encontrará automaticamente <code>?time_duration</code> e listará abaixo. Adicione uma descrição simples para cada um, para ajudar o agente (e outros usuários) a entender sua finalidade.</p><ul><li><code>time_duration</code>O período de tempo para pesquisar notícias negativas. O formato é &quot;X horas&quot;, com o valor padrão de 8760 horas.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" style="width: 100%;" /><p>4. Teste!</p><ul><li>Clique em Salvar e testar.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/57e768b88327821e70bd616744822f98fa367362-732x136.png" style="width: 100%;" /><ul><li>Você verá um novo menu suspenso onde poderá testar a consulta para garantir que ela esteja funcionando conforme o esperado.</li></ul><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; eu. Em <code>time_duration</code> insira o intervalo desejado; aqui, estamos usando “8760 horas”.</p><ul><li>Clique em “Enviar” e, se tudo correr bem, você verá uma resposta em formato JSON. Para garantir que funcione como esperado, role para baixo e observe o objeto <code>values</code> . É aí que os documentos correspondentes são devolvidos.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" style="width: 100%;" /><p>5. Clique no “X” no canto superior direito para fechar a janela de teste. Sua nova ferramenta agora aparecerá na lista, pronta para ser atribuída a um agente.</p><h3>O caminho da API</h3><p>Para desenvolvedores que preferem automação ou precisam gerenciar ferramentas programaticamente, é possível obter o mesmo resultado com uma única chamada de API. Basta enviar uma solicitação <code>POST</code> para o endpoint <code>/api/agent_builder/tools</code> com a definição da ferramenta.</p><pre><code>POST kbn://api/agent_builder/tools
{
  "id": "find_client_exposure_to_negative_news",
  "type": "esql",
  "description": "Finds client portfolio exposure to negative news. This tool scans recent news and reports for negative sentiment, identifies the associated asset, and finds all clients holding that asset. It returns a list sorted by the current market value of the position to highlight the highest potential risk.",
  "configuration": {
    "query": """
        FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) >= NOW() - TO_TIMEDURATION(?time_duration)
        | RENAME primary_symbol AS symbol
        | LOOKUP JOIN financial_asset_details ON symbol
        | LOOKUP JOIN financial_holdings ON symbol
        | LOOKUP JOIN financial_accounts ON account_id
        | WHERE account_holder_name IS NOT NULL
        | EVAL position_current_value = quantity * current_price.price
        | RENAME title AS news_title
        | KEEP
            account_holder_name, symbol, asset_name, news_title,
            sentiment, position_current_value, quantity, current_price.price,
            published_date, report_date
        | SORT position_current_value DESC
        | LIMIT 50
      """,
    "params": {
      "time_duration": {
        "type": "keyword",
        "description": """The timeframe to search back for negative news. Format is "X hours" DEFAULT TO 8760 hours """
      }
    }
  },
  "tags": [
    "retrieval",
    "risk-analysis"
  ]
}</code></pre><h2>Etapa 3: O Cérebro — Criando seu Agente Personalizado</h2><p>Criamos uma habilidade reutilizável (a Ferramenta). Agora, precisamos criar o <strong>Agente</strong>, a persona que de fato irá utilizá-lo. Um Agente é a combinação de um LLM (Licença de Aprendizagem Baseada em Leis), um conjunto específico de ferramentas às quais você lhe concede acesso e, mais importante, um conjunto de <strong>Instruções Personalizadas</strong> que atuam como sua constituição, definindo sua personalidade, regras e propósito.</p><h3>A Arte do Prompt</h3><p>O aspecto mais importante na criação de um agente confiável e especializado é o prompt. Um conjunto de instruções bem elaborado é o que diferencia um chatbot genérico de um assistente profissional e focado. É aqui que você define as diretrizes, define a saída e atribui ao agente sua missão.</p><p>Para o nosso agente <code>Financial Manager</code> , usaremos o seguinte prompt.</p><pre><code>You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**</code></pre><p>Vamos analisar por que essa estratégia é tão eficaz:</p><ul><li><strong>Define uma persona sofisticada: </strong>a primeira frase estabelece imediatamente o agente como um &quot;Assistente de Inteligência de Dados especializado&quot;, definindo um tom profissional e competente.</li><li><strong>Isso fornece uma estrutura de raciocínio: </strong>ao dizer ao agente para &quot;Compreender, Planejar, Executar e Sintetizar&quot;, estamos lhe dando um procedimento operacional padrão. Isso melhora sua capacidade de lidar com questões complexas e de várias etapas.</li><li><strong>Isso promove o diálogo interativo: </strong>a instrução para &quot;fazer perguntas esclarecedoras&quot; torna o agente mais robusto. Isso minimizará suposições incorretas sobre solicitações ambíguas, levando a respostas mais precisas.</li></ul><h3>O Caminho da Interface do Usuário</h3><p>1. Navegue até <strong>Agentes.</strong></p><ul><li>Clique em<strong> Ferramentas </strong>ou <strong>Gerenciar Ferramentas</strong> e clique no botão <strong>Nova ferramenta</strong> .</li></ul><p>2. Preencha os dados básicos:</p><ul><li><strong>ID do agente:</strong> <code>financial_assistant</code>.</li><li><strong>Instruções: </strong>Copie o enunciado acima.</li><li><strong>Rótulos</strong>: <code>Finance</code>.</li><li><strong>Nome de exibição:</strong> <code>Financial Assistant</code>.</li><li><strong>Descrição da exibição: </strong><code>An assistant for analyzing and understanding your financial data</code>.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" style="width: 100%;" /><p>3. De volta ao topo, clique em <strong>Ferramentas</strong>.</p><ul><li>Marque a caixa ao lado da nossa ferramenta <code>find_client_exposure_to_negative_news</code> .</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" style="width: 100%;" /><p>4. Clique em <strong>Salvar</strong>.</p><h3>O caminho da API</h3><p>Você pode criar o mesmo agente com uma solicitação <code>POST</code> para o endpoint <code>/api/agent_builder/agents</code> . O corpo da solicitação contém todas as mesmas informações: o ID, o nome, a descrição, o conjunto completo de instruções e uma lista das ferramentas que o agente tem permissão para usar.</p><pre><code>POST kbn://api/agent_builder/agents
    {
      "id": "financial_assistant",
      "name": "Financial Assistant",
      "description": "An assistant for analyzing and understanding your financial data",
      "labels": [
        "Finance"
      ],
      "avatar_color": "#16C5C0",
      "avatar_symbol": "💰",
      "configuration": {
        "instructions": """You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**
""",
        "tools": [
          {
            "tool_ids": [
              "platform.core.search",
              "platform.core.list_indices",
              "platform.core.get_index_mapping",
              "platform.core.get_document_by_id",
              "find_client_exposure_to_negative_news"
            ]
          }
        ]
      }
    }</code></pre><h2>Passo 4: A Recompensa — Ter uma Conversa</h2><p>Temos nossa lógica de negócios encapsulada em uma ferramenta e um &quot;cérebro&quot; pronto para usá-la em nosso Agente. Chegou a hora de ver tudo se concretizar. Agora podemos começar a interagir com nossos dados usando um agente especializado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" style="width: 100%;" /><h3>O Caminho da Interface do Usuário</h3><ol><li>Navegue até <strong>Agentes </strong>no Kibana.</li><li>Utilizando o menu suspenso no canto inferior direito da janela de chat, alterne do <strong>agente padrão Elastic AI</strong> para o nosso novo agente <strong>Assistente Financeiro </strong> .</li><li>Faça uma pergunta que permita ao agente usar nossa ferramenta especializada:<ol><li><em>Estou preocupado com o sentimento do mercado. Você pode me mostrar quais dos nossos clientes correm maior risco em caso de más notícias?</em></li></ol></li></ol><p>Após alguns instantes, o agente retornará uma resposta completa e perfeitamente formatada. Devido à natureza dos LLMs, sua resposta pode ser formatada de maneira ligeiramente diferente, mas nesta execução, o agente retornou:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" style="width: 100%;" /><h3>O que acabou de acontecer? O Raciocínio do Agente</h3><p>O agente não apenas &quot;sabia&quot; a resposta. Executou um plano de várias etapas centrado na seleção da melhor ferramenta para o trabalho. Eis uma análise do seu processo de pensamento:</p><ul><li><strong>Intenção identificada:</strong> Correspondeu a palavras-chave da sua pergunta, como &quot;risco&quot; e &quot;notícias negativas&quot;, à descrição da ferramenta <code>find_client_exposure_to_negative_news</code> .</li><li><strong>Plano executado:</strong> o sistema extraiu o período de tempo da sua solicitação e fez uma <strong>única chamada</strong> para essa ferramenta especializada.</li><li><strong>Delegou o trabalho:</strong> a ferramenta então realizou todo o trabalho pesado: as junções encadeadas, os cálculos de valor e a classificação.</li><li><strong>Resultado Sintetizado:</strong> Por fim, o agente formatou os dados brutos da ferramenta em um resumo claro e legível para humanos, seguindo as regras do prompt.</li></ul><p>E não precisamos apenas supor, se ampliarmos nosso pensamento e observarmos mais detalhes.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" style="width: 100%;" /><h3>O caminho da API</h3><p>Você pode iniciar essa mesma conversa programaticamente. Basta enviar a pergunta de entrada para o endpoint da API <code>converse</code> , certificando-se de especificar o <code>agent_id</code> do nosso <code>financial_manager</code>.</p><pre><code>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}</code></pre><h2>Para desenvolvedores: Integração com a API</h2><p>Embora a interface do Kibana ofereça uma experiência fantástica e intuitiva para criar e gerenciar seus agentes, tudo o que você viu hoje também pode ser feito programaticamente. O Agent Builder é baseado em um conjunto de APIs, permitindo que você integre essa funcionalidade diretamente em seus próprios aplicativos, pipelines de CI/CD ou scripts de automação.</p><p>Os três principais endpoints com os quais você trabalhará são:</p><ul><li><strong><code>/api/agent_builder/tools</code></strong>O ponto de extremidade para criar, listar e gerenciar as habilidades reutilizáveis que seus agentes podem usar.</li><li><strong><code>/api/agent_builder/agents</code></strong>O ponto final para definir as personas dos seus agentes, incluindo as importantíssimas instruções e atribuições de ferramentas.</li><li><strong><code>/api/agent_builder/converse</code></strong>: O ponto de acesso para interagir com seus agentes, iniciar conversas e obter respostas.</li></ul><p>Para um passo a passo completo e prático de como usar essas APIs para executar cada etapa deste tutorial, confira o <strong>Jupyter Notebook</strong> que acompanha o tutorial, disponível <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">aqui</a> em nosso repositório do GitHub.</p><h2>Conclusão: Sua vez de construir</h2><p>Começamos por pegar numa consulta ES|QL e transformá-la numa habilidade reutilizável. Em seguida, criamos um agente de IA especializado, atribuindo-lhe uma missão e regras claras, e capacitando-o com essa habilidade. O resultado é um assistente sofisticado que consegue entender uma pergunta complexa e executar uma análise em várias etapas para fornecer uma resposta precisa e baseada em dados.</p><p>Esse fluxo de trabalho é fundamental para o novo <strong>Construtor de Agentes</strong> da Elastic. Ele foi projetado para ser simples o suficiente para que usuários sem conhecimento técnico possam criar agentes por meio da interface do usuário, mas também sofisticado o bastante para que desenvolvedores criem aplicativos personalizados com inteligência artificial utilizando nossas APIs. Mais importante ainda, permite que você conecte LLMs aos seus próprios dados de forma segura e protegida, regida pela lógica especializada que você define, e converse com seus dados.</p><h2>Pronto para usar agentes para conversar com seus dados?</h2><p>A melhor maneira de consolidar o que você aprendeu é colocar a mão na massa. Experimente tudo o que discutimos hoje em nossa <a href="https://www.elastic.co/training/elastic-ai-agents-mcp"><strong>oficina prática, gratuita e interativa</strong></a>. Você passará por todo esse processo e muito mais em um ambiente sandbox dedicado.</p><p>Em um post futuro do blog, mostraremos como usar um aplicativo independente que interage com nosso agente <code>Financial Assistant</code> e exploraremos o <strong>Protocolo de Contexto de Modelo (MCP)</strong> que torna tudo isso possível. E em um post separado, discutiremos o suporte do Agent Builder ao protocolo Agent2Agent, ou A2A, ainda em desenvolvimento.</p><p>Fiquem ligados e boas construções!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando fluxos de trabalho com agentes de IA usando o Elasticsearch.]]></title>
            <description><![CDATA[Conheça o Agent Builder, uma nova camada de IA no Elasticsearch que fornece uma estrutura para a criação de fluxos de trabalho de IA baseados em agentes, usando pesquisa híbrida para fornecer aos agentes o contexto necessário para raciocinar e agir.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Anish Mathur, Dana Juratoni]]></dc:creator>
            <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5d04bbcfc3925f48f3487ea4c7e0dd2205316d0-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Aqui na Elastic, temos vindo a trazer contexto para LLMs e interfaces conversacionais com Assistentes de IA, RAG avançado e melhorias na base de dados vetorial. Recentemente, com o surgimento de agentes de IA, vimos crescer a necessidade de contexto relevante e aprendemos que<strong> agentes de IA de alto impacto precisam de uma ótima ferramenta de busca</strong>. Por isso, criamos novas funcionalidades nativas no Elastic Stack, projetadas para ajudar no desenvolvimento de agentes de IA que aproveitam seus dados no Elasticsearch. Gostaríamos de compartilhar nosso progresso nessa jornada e para onde vemos que ela nos levará no futuro.</p><h2>Construtor de Agentes: Uma Base para a Criação de Agentes de IA Orientados por Dados</h2><p>A promessa de um agente de IA é simples: dê a ele um objetivo e ele realizará a tarefa. Mas para os desenvolvedores, a realidade é uma série de desafios complexos. Em primeiro lugar, um agente é tão bom quanto a sua percepção do ambiente e das ferramentas que lhe são fornecidas para atingir os objetivos do usuário. Além disso, fornecer o contexto correto em meio a um mar de dados empresariais diversos é um desafio enorme. Finalmente, tudo isso precisa ser orquestrado por um circuito de raciocínio confiável que possa planejar, executar e aprender.</p><p>Para resolver isso, os desenvolvedores precisam construir uma estrutura complexa e frágil do zero. A arquitetura de agentes atual exige a integração de várias peças distintas: um LLM (Modelo de Aprendizado de Liderança), um banco de dados vetorial, um repositório de metadados, sistemas separados para registro e rastreamento, e alguma forma de avaliar se tudo está funcionando corretamente. Isso não é apenas complexo; é caro, propenso a erros e dificulta a criação de sistemas de IA confiáveis e de alta qualidade que seus usuários exigem.</p><p>Por isso, queremos simplificar. Para isso, nossa abordagem consiste em pegar os elementos essenciais de um agente orientado ao contexto eficaz e integrá-los diretamente ao núcleo do Elasticsearch com um novo conjunto de recursos chamado <strong>Elastic AI Agent Builder</strong>. Essa nova camada fornece uma estrutura com todos os componentes essenciais para a criação de agentes de IA baseados no Elasticsearch: um conjunto aberto de primitivas, protocolos baseados em padrões e acesso seguro aos dados — para que você possa criar sistemas de agentes adaptados a dados e requisitos do mundo real:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" style="width: 100%;" /><p><strong>Proporcionar experiências com IA</strong>: esse é o objetivo final. Com nossa Plataforma de IA de Busca e seus dados como base, você pode criar qualquer tipo de aplicativo de IA generativa: desde interfaces de bate-papo personalizadas até integrações com frameworks de agentes como o LangChain ou aplicativos de negócios como o Salesforce.</p><p><strong>Com tecnologia Agents &amp; Tools</strong>: sobre a plataforma, expomos uma camada de abstrações limpa e simples. Você interage diretamente com agentes e ferramentas, que podem ser personalizadas para atender às suas necessidades específicas. Você também pode acessar os recursos da plataforma por meio de APIs robustas e padrões abertos como MCP e A2A.</p><p><strong>Habilitado pela Plataforma de IA de Busca</strong>: este é o mecanismo principal onde integramos os componentes. O banco de dados vetorial avançado, a lógica do agente, a construção de consultas, os recursos de segurança e o rastreamento para avaliação, tudo reside aqui, gerenciado e otimizado pela Elastic.</p><p><strong>Desvendando o poder dos seus dados</strong>: a base de qualquer agente de sucesso são dados de alta qualidade. Nossa plataforma começa com a capacidade de ingerir ou federar o acesso a todos os dados da sua empresa.</p><h2>Construção de Agentes na Plataforma</h2><p>O Agent Builder, integrado à Plataforma de IA de Busca, fornece uma estrutura completa para o desenvolvimento de agentes. É construído sobre cinco pilares fundamentais, cada um projetado para abordar um aspecto crítico da construção e implantação de sistemas de IA de nível de produção. Vamos analisar como os agentes definem o objetivo, as ferramentas fornecem as capacidades, os padrões abertos garantem a interoperabilidade, a avaliação proporciona transparência e a segurança garante a confiança.</p><h3>Agentes</h3><p>Os agentes são o bloco de construção de nível mais alto nesta nova camada do Elasticsearch. Um agente define o objetivo a ser alcançado, o conjunto de ferramentas disponíveis para execução e as fontes de dados sobre as quais pode operar. Os agentes não se limitam a interações conversacionais; eles podem viabilizar fluxos de trabalho completos, automação de tarefas ou experiências voltadas para o usuário.</p><p>Quando uma consulta é direcionada a um agente, ela segue um ciclo estruturado:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" style="width: 100%;" /><ol><li>Interprete sua contribuição e objetivo.</li><li>Selecione a ferramenta e os argumentos corretos para a execução.</li><li>Analise a resposta da ferramenta.</li><li>Decida se deseja retornar um resultado ou continuar com outras invocações da ferramenta.</li></ol><p>A Elastic cuida da orquestração, do contexto e da execução desse ciclo. Os desenvolvedores se concentram em definir <em>o que</em> o agente deve fazer: objetivos, ferramentas e dados, enquanto o sistema gerencia <em>como</em> o raciocínio e os fluxos de trabalho são executados.</p><p><em>O Agente Padrão</em></p><p>Nosso primeiro agente desenvolvido nesta plataforma é um agente conversacional nativo do Kibana, que permite interagir imediatamente com seus dados. Proporciona uma experiência pronta a usar, mantendo-se totalmente extensível e permitindo que você comece a interagir com seus dados imediatamente, sem necessidade de configuração adicional.</p><p>Você pode interagir com essa experiência diretamente no Kibana por meio de uma nova experiência de chat ou via API.</p><p>Consultar o agente padrão por meio da API requer apenas uma única chamada:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}</code></pre><p>Como as conversas mantêm estado, você pode continuar interagindo com um agente usando um `conversation_id` ou recuperar o histórico completo da conversa:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "What about the second top?",
    "conversation_id": "ec757c6c-c3ed-4a83-8e2c-756238f008bb"
}

## get the full conversation
GET kbn://api/agent_builder/conversations/ec757c6c-c3ed-4a83-8e2c-756238f008bb</code></pre><p><em>Agentes alfandegários</em></p><p>Os desenvolvedores também podem criar seus próprios agentes personalizados por meio de APIs simples. Os agentes encapsulam instruções, ferramentas e acesso a dados, criando mecanismos de raciocínio personalizados.</p><p>Criar um agente personalizado é tão simples quanto fazer uma única chamada à API. O exemplo abaixo ilustra isso. O campo &quot;configuração&quot; contém todos os detalhes importantes, como instruções ou ferramentas disponíveis:</p><pre><code>POST kbn://api/agent_builder/agents
{
  "id": "custom_agent",
  "name": "My Custom Agent",
  "description": "Description of the custom agent",
  "configuration": {
      "instructions": "You are a log expert specialising in ...",
      "tools": 
...
   }
}</code></pre><p>Uma vez criado, o agente pode ser consultado diretamente:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}</code></pre><p>Essa abordagem transforma o agente, de um sistema complexo a ser construído do zero, em uma unidade simples e declarativa de lógica de negócios, permitindo que você implemente automação inteligente mais rapidamente.</p><p>Para uma análise aprofundada sobre como construir um agente especializado do zero, consulte nosso guia detalhado, passo a passo: <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Seu primeiro agente elástico: de uma única consulta a um bate-papo com inteligência artificial</a>.</p><h3>Ferramentas</h3><p>Se os agentes definem <em>o que</em> realizar, as ferramentas definem <em>como</em>.</p><p>As ferramentas expõem funcionalidades específicas do Elastic Core para que os agentes executem e recuperem informações ou realizem uma ação. As ferramentas podem incluir funcionalidades básicas como obter índices ou obter mapeamentos, ou funcionalidades mais avançadas como conversão de linguagem natural para ES|QL.</p><p>O Elasticsearch é fornecido com um conjunto de ferramentas padrão otimizadas para necessidades comuns. Mas a verdadeira flexibilidade vem de criar a sua própria. Ao definir as ferramentas, você decide exatamente quais consultas, índices e campos são expostos a um agente com ES|QL, proporcionando controle preciso sobre velocidade, exatidão e segurança.</p><p>O registro de uma nova ferramenta também é tão simples quanto uma única chamada de API. Você poderia criar uma ferramenta que utilizasse nossa linguagem <a href="https://www.elastic.co/search-labs/blog/esql-timeline-of-improvements">ES|QL (Elasticsearch Query Language)</a> para encontrar notícias sobre um ativo financeiro específico:</p><pre><code>POST kbn://api/agent_builder/tools
{
  "id": "news_on_asset",
  "type": "esql",
  "description": "Find news and reports about a particular asset where ...",
  "configuration": {
    "query": "FROM financial_news, financial_reports | where MATCH(company_symbol, ?symbol) OR MATCH(entities, ?symbol) | limit 5",
    "params": {
      "symbol": {
        "type": "keyword",
        "description": "The asset symbol"
      }
    }
  ...
  }
...
}</code></pre><p>Após o registro, você pode atribuir a nova ferramenta aos seus agentes personalizados, oferecendo a eles um conjunto selecionado de habilidades para analisar e utilizar sempre que for adequado.</p><p>Oferecemos uma plataforma para criar ferramentas personalizadas para suas necessidades específicas, por exemplo, com ES|QL, que transforma o agente de um agente de propósito geral em um especialista em um domínio específico, fundamentado em seus dados e domínio de negócios exclusivos.</p><h3>Padrões Abertos e Interoperabilidade</h3><p>Os agentes e ferramentas do Elasticsearch são expostos por meio de APIs de padrão aberto, o que facilita sua integração como blocos fundamentais dentro do ecossistema mais amplo de frameworks de agentes. Nossa abordagem é simples: sem caixas pretas. Queremos que você possa aproveitar o principal ponto forte da Elastic em buscas e combiná-lo com recursos complementares e outros sistemas de agentes.</p><p>Para tornar isso possível, estamos disponibilizando nossas capacidades por meio de APIs, protocolos emergentes e padrões abertos.</p><p><em>Protocolo de Contexto do Modelo (MCP)</em></p><p><a href="https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch">O Protocolo de Contexto de Modelo (MCP)</a> está rapidamente se tornando o padrão aberto para conectar ferramentas em diferentes sistemas. Ao oferecer suporte ao MCP, o Elasticsearch pode conectar a IA conversacional aos seus bancos de dados, índices e APIs externas. Com um servidor MCP remoto integrado ao Elastic Stack, qualquer cliente compatível com MCP pode acessar as ferramentas da Elastic e usá-las como blocos de construção em seus fluxos de trabalho de agentes mais amplos.</p><p>Esta não é uma via de mão única. Você também poderá importar ferramentas de servidores MCP externos e disponibilizá-las dentro do Elasticsearch. Em breve, os servidores MCP provavelmente estarão disponíveis para quase tudo e serão muito mais abrangentes do que qualquer coisa que pudéssemos criar por conta própria. A Elastic oferece busca e recuperação em grande escala, e você pode combinar isso com recursos especializados de outras plataformas para criar agentes eficazes.</p><p><em>Agente para Agente (A2A)</em></p><p>Também estamos trabalhando no suporte de agente para agente (A2A). Enquanto o MCP se concentra em conectar ferramentas, o A2A se concentra em conectar agentes. Com um servidor A2A, os agentes Elastic que você criar poderão se comunicar diretamente com agentes de outros sistemas: compartilhando contexto, delegando tarefas e coordenando fluxos de trabalho.</p><p>Pense nisso como interoperabilidade na camada de raciocínio. Seu agente Elastic pode lidar com a busca e recuperação de dados, depois repassar a tarefa para um agente de suporte ou de TI especializado e obter o resultado de volta sem problemas. O resultado é um ecossistema de agentes cooperativos, cada um fazendo o que faz de melhor.</p><p>Em última análise, a adoção do MCP e do A2A reforça nosso compromisso com o papel do Elasticsearch como um elemento de primeira classe, garantindo a integração aberta em todo o ecossistema de agentes.</p><h3>Rastreamento e Avaliação</h3><p>À medida que a busca se integra aos agentes, o desafio da avaliação eficaz torna-se crucial. Para implantar agentes com segurança em ambientes empresariais reais, você precisa ter a garantia de que eles não sejam apenas precisos, mas também eficientes e confiáveis. Como você mede o desempenho, diagnostica uma resposta inadequada ou melhora o nível inicial? Tudo começa com a visibilidade.</p><p>É por isso que projetamos nossas APIs de agentes com foco na transparência desde o início. Considere esta interação simples entre agentes:</p><pre><code>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}</code></pre><p>A resposta inclui não apenas a resposta final, mas também o rastreamento completo da execução, detalhando quais ferramentas o agente selecionou, os parâmetros que utilizou e os resultados de cada etapa.</p><pre><code>{
  "conversation_id": "db5c0c8b-12bf-4928-a57e-d99129ad2fea",
  "steps": [
    {
      "type": "tool_call",
      "tool_call_id": "tooluse_Nfqr3mwtR92HTRIsTcGXZQ",
      "tool_id": ".index_explorer",
      "params": {
        "query": "indices containing portfolio data"
      },
      "results": [...]
    }
    // ... more steps ...
  ],
  "response": {
    "message": "Based on the information I've gathered...."
  }
}</code></pre><p>O rastreamento e o registro abrangentes são essenciais para um ciclo de melhoria contínua e, em breve, você poderá armazenar e visualizar esses rastreamentos de agentes diretamente no Elasticsearch. Melhor ainda, esses rastreamentos são baseados no protocolo OpenTelemetry, garantindo que sejam padronizados e portáteis para integração com a plataforma de observabilidade de sua escolha.</p><p>Esse nível de detalhamento é a base para um verdadeiro ciclo de melhoria contínua. Ele permite que você crie um conjunto abrangente de testes, depure falhas, identifique modos de falha para evitar regressões e capture padrões de sucesso para otimizar o desempenho. Em última análise, essa abordagem orientada por dados é a chave para transformar um protótipo promissor em um sistema de IA confiável e pronto para produção.</p><h3>Segurança</h3><p>À medida que os agentes e as ferramentas se tornam mais capazes, a segurança deixa de ser opcional e passa a ser fundamental. Expor APIs, automatizar tarefas e fluxos de trabalho exige que os sistemas empresariais sejam confiáveis. Principalmente à medida que os agentes começam a automatizar mais fluxos de trabalho, a capacidade de protegê-los e garantir que atendam aos requisitos da empresa torna-se essencial.</p><p>Todas as funcionalidades acima herdam os controles já disponíveis no Elastic atualmente, incluindo <a href="https://www.elastic.co/search-labs/blog/rag-and-rbac-integration">o controle de acesso baseado em funções (RBAC)</a> para chamadas de API e o gerenciamento de chaves de API. Também estamos estendendo os mesmos controles a novos protocolos como o MCP. Isso significa suporte para padrões como o OAuth, bem como a capacidade de integrar mecanismos de autenticação personalizados.</p><p>Nosso objetivo é oferecer a flexibilidade necessária para que você experimente agentes e ferramentas, mantendo o nível de segurança, conformidade e governança que sua organização exige.</p><h2>O que vem a seguir</h2><p>Não estamos apenas adicionando funcionalidades; estamos expandindo o Elasticsearch para engenharia de contexto agente. Planejamos desenvolver nosso trabalho daqui para frente com base nesses princípios:</p><p>1. Compromisso com o código aberto e os padrões</p><p>Nosso compromisso com o código aberto e os padrões abertos garante que essas funcionalidades permaneçam interoperáveis com estruturas de agentes externas. Você sempre poderá conectar, estender e compor agentes em todo o seu ecossistema, mantendo seus dados e fluxos de trabalho sob seu controle.</p><p>2. Valor do Contexto</p><p>O contexto é o maior trunfo de um agente de IA. Gerenciar o contexto enquanto os agentes realizam buscas e operações de fluxo de trabalho pode ser uma tarefa desafiadora. Estamos aproveitando os principais pontos fortes da Elastic para resolver a engenharia de contexto, garantindo que as informações mais relevantes estejam sempre disponíveis para o seu agente.</p><p>3. Foque em fluxos de dados agéticos</p><p>No futuro, os agentes serão uma fonte de dados cada vez maior, incluindo a saída dos agentes (documentos gerados, relatórios, visualizações) e o rastro de execução dos agentes (seu raciocínio, chamadas de ferramentas, memória/contexto). A Elastic é ideal para lidar com esse tipo de dados, e estamos trabalhando em pesquisas sobre como realizar análises, avaliações e melhorias automatizadas usando esses dados.</p><p>4. Segurança e proteção por design</p><p>Os agentes de IA introduzem um conjunto totalmente novo de desafios em termos de segurança e proteção. A Elastic sempre foi líder em soluções seguras e continuamos a incorporar proteções de nível empresarial, controles de acesso e princípios de &quot;confiança zero&quot;.</p><p>5. Integrado à plataforma</p><p>Os recursos para criar agentes de IA estão integrados na plataforma Elasticsearch. Isso significa que funcionalidades de nível de plataforma, como rastreamento, avaliação, visualização e análise, são todas aplicáveis aos agentes. Deseja desenvolver painéis de controle com base nas execuções dos agentes? Isso já está integrado. Deseja avaliar o desempenho do agente de IA usando análise de sentimentos? A plataforma permite isso. Isso possibilita a criação de um ciclo de vida completo em torno de suas experiências com IA.</p><p>O objetivo da Elastic é fornecer interfaces para que você possa criar IA conversacional e fluxos de trabalho automatizados que sejam totalmente integrados, extensíveis e baseados em seus dados. Mais detalhes técnicos e informações sobre o progresso serão compartilhados em breve.</p><p>O Construtor de Agentes já está disponível em versão prévia privada. <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">Entre em contato conosco</a> para solicitar acesso. Tem perguntas ou comentários? Conecte-se com nossa comunidade de desenvolvedores em nosso <a href="https://elasticstack.slack.com/archives/C09GRHEQ4AG"><strong>espaço de trabalho no Slack</strong></a> ou em nosso <a href="https://discuss.elastic.co/c/search/84"><strong>fórum de discussão</strong></a>.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elastic Open Web Crawler como código]]></title>
            <description><![CDATA[Aprenda a usar o GitHub Actions para gerenciar as configurações do Elastic Open Crawler, de forma que, sempre que enviarmos alterações para o repositório, elas sejam aplicadas automaticamente à instância implantada do crawler.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elastic-open-crawler-config-as-code</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elastic-open-crawler-config-as-code</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Gustavo Llermaly]]></dc:creator>
            <pubDate>Mon, 22 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ac41f55c85ad3f647cb0e0d750ed80bacd397f3-1036x581.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Com <a href="https://github.com/elastic/crawler">o Elastic Open Web Crawler</a> e sua arquitetura orientada por linha de comando, ter configurações de crawler versionadas e um pipeline de CI/CD com testes locais agora é bastante simples de se obter.</p><p>Tradicionalmente, o gerenciamento de rastreadores era um processo manual e propenso a erros. Isso envolvia editar configurações diretamente na interface do usuário e ter dificuldades com a clonagem de configurações de rastreamento, reversão, controle de versão e muito mais. Tratar as configurações do rastreador como código resolve isso, proporcionando os mesmos benefícios que esperamos no desenvolvimento de software: repetibilidade, rastreabilidade e automação.</p><p>Esse fluxo de trabalho facilita a integração do Open Web Crawler ao seu pipeline de CI/CD para reversões, backups e migrações — tarefas que eram muito mais complicadas com versões anteriores do Elastic Crawler, como o Elastic Web Crawler ou o App Search Crawler.</p><p>Neste artigo, vamos aprender como:</p><ul><li>Gerencie nossas configurações de rastreamento usando o GitHub.</li><li>Ter um ambiente local para testar pipelines antes da implantação.</li><li>Criar um ambiente de produção para executar o rastreador web com novas configurações sempre que enviarmos alterações para nossa branch principal.</li></ul><p>Você pode encontrar o repositório do projeto <em><strong><a href="https://github.com/llermaly/elastic-open-crawler-as-code">aqui</a>. </strong>No momento em que escrevo, estou usando o Elasticsearch 9.1.3 e o Open Web Crawler 0.4.2.</em></p><h2>Pré-requisitos</h2><ul><li>Docker Desktop</li><li>instância do Elasticsearch</li><li>Máquina virtual com acesso SSH (por exemplo, AWS EC2) e Docker instalado.</li></ul><h2>Etapas</h2><ol><li>Estrutura de pastas</li><li>Configuração do rastreador</li><li>Arquivo Docker-compose (ambiente local)</li><li>Ações do GitHub</li><li>Testando localmente</li><li>Implantação em produção</li><li>Realizar alterações e redistribuir</li></ol><h2>Estrutura de pastas</h2><p>Para este projeto, teremos a seguinte estrutura de arquivos:</p><pre><code>├── docker-compose.yml # Local elasticsearch + crawler
├── config/crawler-config.yml # Crawler config
├── .github/workflows/deploy.yml # GH Action to deploy changes
├── local.sh # Script to run our local crawler</code></pre><h2>Configuração do rastreador</h2><p>Em <code>crawler-config.yml,</code> colocaremos o seguinte:</p><pre><code>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3</code></pre><p>Este script irá extrair dados de <a href="https://web-scraping.dev/products">https://web-scraping.dev/products</a>, um site fictício para produtos. Iremos rastrear apenas as três primeiras páginas de produtos. A configuração <code>max_crawl_depth</code> impedirá que o rastreador descubra mais páginas do que as definidas como <code>seed_urls</code> , não abrindo os links dentro delas.</p><p>Elasticsearch <code>host</code> e <code>api_key</code> serão preenchidos dinamicamente dependendo do ambiente em que estamos executando o script.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f9831034e1c4ccb554d37bdd188f2824338355a0-890x624.png" style="width: 100%;" /><h2>Arquivo Docker-compose (ambiente local)</h2><p>Para o ambiente local <code>docker-compose.yml,</code> implantaremos o rastreador e um único cluster Elasticsearch + Kibana, para que possamos visualizar facilmente os resultados do rastreamento <strong><em>antes</em></strong> da implantação em produção.</p><pre><code>services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:9.1.3
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
    ports:
      - "9200:9200"
    networks: [esnet]
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 5s
      timeout: 5s
      retries: 10

  kibana:
    image: docker.elastic.co/kibana/kibana:9.1.3
    environment:
      - ELASTICSEARCH_HOSTS=http://es01:9200
    ports:
      - "5601:5601"
    networks: [esnet]
    depends_on: [es01]

  crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    stdin_open: true
    tty: true

networks:
  esnet:
    driver: bridge</code></pre><p>Observe como o rastreador aguarda até que o Elasticsearch esteja pronto para ser executado.</p><h2>Ações do GitHub</h2><p>Agora precisamos criar uma ação do GitHub que copie as novas configurações e execute o rastreador em nossa máquina virtual a cada push para o repositório principal. Isso garante que sempre tenhamos a configuração mais recente implantada, sem precisar entrar manualmente na máquina virtual para atualizar arquivos e executar o rastreador. Vamos usar o AWS EC2 como provedor de máquinas virtuais.</p><p>O primeiro passo é adicionar o host (<code>VM_HOST</code>), o usuário da máquina (<code>VM_USER</code>), a chave SSH RSA (<code>VM_KEY</code>), o host do Elasticsearch (<code>ES_HOST</code>) e a chave da API do Elasticsearch (<code>ES_API_KEY</code>) aos segredos da ação do GitHub:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e1075bc54151b4b94eac2a6bd2682e9997e6c709-1106x707.png" style="width: 100%;" /><p>Dessa forma, a ação poderá acessar nosso servidor para copiar os novos arquivos e executar a indexação.</p><p>Agora, vamos criar nosso arquivo <code>.github/workflows/deploy.yml</code> :</p><pre><code>name: Deploy

on:
  push:
    branches: [main]

jobs:
  Deploy:
    name: Deploy to EC2
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5

      - name: Deploy crawler
        env:
          HOSTNAME: ${{ secrets.VM_HOST }}
          USER_NAME: ${{ secrets.VM_USER }}
          PRIVATE_KEY: ${{ secrets.VM_KEY }}
          ES_HOST: ${{ secrets.ES_HOST }}
          ES_API_KEY: ${{ secrets.ES_API_KEY }}
        run: |
          # Save private key
          echo "$PRIVATE_KEY" > private_key
          chmod 600 private_key

          # Generate final config locally
          envsubst < config/crawler-config.yml > config/crawl-config-final.yml

          # Copy the config folder to VM
          scp -o StrictHostKeyChecking=no -i private_key -r config ${USER_NAME}@${HOSTNAME}:~/config

          # SSH into VM and run crawler
          ssh -o StrictHostKeyChecking=no -i private_key ${USER_NAME}@${HOSTNAME} << EOF
            docker run --rm \
              -v ~/config:/config \
              docker.elastic.co/integrations/crawler:latest jruby \
              bin/crawler crawl /config/crawl-config-final.yml
          EOF</code></pre><p>Essa ação executará os seguintes passos sempre que enviarmos alterações para o arquivo de configuração do rastreador:</p><ol><li>Preencha o arquivo de configuração YAML com o host e a chave da API do Elasticsearch.</li><li>Copie a pasta de configuração para a nossa máquina virtual.</li><li>Conecte-se à nossa VM via SSH.</li><li>Execute o rastreamento com a configuração que acabamos de copiar do repositório.</li></ol><h2>Testando localmente</h2><p>Para testar nosso rastreador localmente, criamos um script bash que popula o host do Elasticsearch com a versão local do Docker e inicia uma busca. Você pode executar <code>./local.sh</code> para executá-lo.</p><pre><code>#!/bin/bash

# Exit on any error
set -e

# Load environment variables
export ES_HOST="http://es01:9200"

# Generate final crawler config
envsubst < ./config/crawler-config.yml > ./config/crawl-config-final.yml

# Bring everything up
docker compose up --build</code></pre><p>Vamos verificar as Ferramentas de Desenvolvedor do Kibana para confirmar se o campo<code> web-crawler-index</code> foi preenchido corretamente:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/18551635e8265866e389a9632c4e4540958e4468-990x723.png" style="width: 100%;" /><h2>Implantação em produção</h2><p>Agora estamos prontos para enviar a alteração para a branch principal, o que implantará o crawler em sua máquina virtual e começará a enviar logs para sua instância do Elasticsearch Serverless.</p><pre><code>git add .
git commit -m "First commit"
git push</code></pre><p>Isso acionará a ação do GitHub, que executará o script de implantação na máquina virtual e iniciará a indexação.</p><p>Você pode confirmar se a ação foi executada acessando o repositório do GitHub e visitando a aba “Ações”:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/67ba3a7164d7a8049fe5661264820826cb18ed64-667x325.png" style="width: 100%;" /><h2>Realizar alterações e redistribuir</h2><p>Algo que você pode ter notado é que o <code>price</code> de cada produto faz parte do corpo do documento. O ideal seria armazenar o preço em um campo separado para que pudéssemos aplicar filtros a ele.</p><p>Vamos adicionar essa alteração ao arquivo <code>crawler.yml</code> para usar <a href="https://github.com/elastic/crawler/blob/main/docs/features/EXTRACTION_RULES.md">regras de extração</a> para extrair o preço da classe CSS <code>product-price</code> :</p><pre><code>output_sink: elasticsearch
output_index: web-crawl-index
max_crawl_depth: 1

elasticsearch:
  host: ${ES_HOST}
  api_key: ${ES_API_KEY}
     
  # Index ingest pipeline to process documents before indexing          
  pipeline_enabled: true
  pipeline: pricing-pipeline

domains:
  - url: https://web-scraping.dev
    seed_urls:
      - https://web-scraping.dev/product/1
      - https://web-scraping.dev/product/2
      - https://web-scraping.dev/product/3
    extraction_rulesets:
      - url_filters:
          - type: ends
            pattern: /product/*
        rules:
          - action: extract
            field_name: price
            selector: .product-price
            join_as: string
            source: html</code></pre><p>Também vemos que o preço inclui um sinal de dólar (<code>$</code>), que devemos remover se quisermos executar consultas de intervalo. Podemos usar um pipeline de ingestão para isso. Observe que estamos fazendo referência a ele em nosso novo arquivo de configuração do rastreador acima:</p><pre><code>PUT _ingest/pipeline/pricing-pipeline
{
  "processors": [
    {
      "script": {
        "source": """
                ctx['price'] = ctx['price'].replace("$","")
            """
      }
    }
  ]
}</code></pre><p>Podemos executar esse comando em nosso cluster Elasticsearch de produção. Para o desenvolvimento, como é efêmero, podemos fazer com que a criação do pipeline faça parte do arquivo <code>docker-compose.yml</code> adicionando o seguinte serviço. Observe que também adicionamos um <code>depends_on</code> ao serviço de rastreamento para que ele seja iniciado após a criação bem-sucedida do pipeline.</p><pre><code> crawler:
    image: docker.elastic.co/integrations/crawler:0.4.2
    environment:
      - ES_HOST=http://es01:9200
      - CRAWLER_JRUBY_OPTS=--server
    container_name: crawler
    volumes:
      - ./config:/home/app/config
    networks: [esnet]
    entrypoint: ["/home/app/bin/crawler", "crawl", "/home/app/config/crawl-config-final.yml"]
    depends_on:
      pipeline-init:
        condition: service_completed_successfully
    stdin_open: true
    tty: true  


  pipeline-init:
    image: curlimages/curl:latest
    depends_on:
      es01:
        condition: service_healthy
    networks: [esnet]
    entrypoint: >
        sh -c "
        echo 'Creating ingest pipeline...';
        curl -s -X PUT http://es01:9200/_ingest/pipeline/pricing-pipeline \\
          -H 'Content-Type: application/json' \\
          -d '{\"processors\":[{\"script\":{\"source\":\"ctx.price = ctx.price.replace(\\\"$\\\", \\\"\\\")\"}}]}';
        echo 'Pipeline created!';
        "</code></pre><p>Agora vamos executar <code>`./local.sh`</code> para ver a alteração localmente:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/dc1801599344a9f69f072b07ff828c4ba3815d7b-738x473.png" style="width: 100%;" /><p>Ótimo! Agora vamos impulsionar a mudança:</p><pre><code>git add crawler-config.yml
git commit -m "added price CSS selector"
git push</code></pre><p>Para confirmar se tudo funciona corretamente, você pode verificar seu Kibana de produção, que deverá refletir as alterações e mostrar o preço como um novo campo sem o símbolo de dólar.</p><h2>Conclusão</h2><p>O Elastic Open Web Crawler permite que você gerencie seu crawler como código, o que significa que você pode automatizar todo o pipeline — do desenvolvimento à implantação — e adicionar ambientes locais efêmeros e testes com os dados rastreados programaticamente, para citar alguns exemplos.</p><p>Você está convidado a clonar o repositório oficial e começar a indexar seus próprios dados usando este fluxo de trabalho. Você também pode ler <a href="https://www.elastic.co/search-labs/blog/semantic-search-open-crawler">este artigo</a> para aprender como executar uma pesquisa semântica em índices produzidos pelo rastreador.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Experiência do editor ES|QL do Elasticsearch versus o analisador de eventos PPL do OpenSearch.]]></title>
            <description><![CDATA[Descubra como os recursos avançados do ES|QL Editor aceleram seu fluxo de trabalho, em contraste direto com a abordagem manual do PPL Event Analyzer da OpenSearch. 
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/opensearch-vs-elasticsearch-ppl-esql</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/opensearch-vs-elasticsearch-ppl-esql</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Libby Lin, George Kobar]]></dc:creator>
            <pubDate>Thu, 18 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f1ff4ff4a00b3e5b084d4116cea6cabc82a2d816-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A <a href="https://www.elastic.co/blog/getting-started-elasticsearch-query-language">Linguagem de Consulta Elasticsearch</a> (ES|QL), disponível ao público em geral desde a versão 8.14, introduz uma linguagem e um mecanismo de consulta desenvolvidos especificamente para pesquisa, observabilidade e investigações de segurança. Ao contrário da Piped Processing Language (PPL) do OpenSearch, que se baseia fortemente em linguagens de processamento em pipeline já existentes, o ES|QL foi construído do zero com foco em refinamento, usabilidade e integração perfeita em toda a plataforma Kibana.</p><p>Neste blog, exploraremos a experiência do desenvolvedor com o Editor ES|QL no Elasticsearch 9.1, comparando-a com o PPL no Analisador de Eventos (PPL, na sigla em inglês) do OpenSearch 3.2.</p><p>As diferenças tornam-se rapidamente evidentes: o Editor ES|QL oferece preenchimento automático inteligente, ajuda contextual, consultas recomendadas e suporte a consultas entre clusters, capacitando não apenas usuários iniciantes, mas também usuários de nível especialista. O design cuidadoso para a criação de ES|QL também se reflete na inspeção integrada de consultas e na integração holística por meio de fluxos de trabalho do Kibana, por exemplo, com as Consultas Recentes.</p><p>Em contrapartida, o PPL carece de suporte comparável para autocompletar, orientação contextual e consultas distribuídas, criando uma curva de aprendizado mais acentuada e exigindo mais tentativas e erros.</p><h2>Tornando o ES|QL mais fácil de aprender e usar.</h2><p>Começar a usar uma nova linguagem de consulta pode muitas vezes parecer algo assustador. O editor ES|QL<strong>, </strong>integrado diretamente ao <strong>Kibana Discover</strong>, foi projetado para facilitar esse processo, oferecendo suporte não apenas à criação e depuração de consultas, mas também acelerando a sua familiarização e o seu domínio da linguagem. À medida que o editor ajuda a reduzir o atrito nas tarefas diárias, você pode mudar o foco da sintaxe e da tentativa e erro para a busca de soluções. Você pode ler mais sobre esses princípios e como os integramos ao editor <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">aqui</a>.</p><p>Essa experiência de edição não se limita ao Discover; trata-se de um módulo de código reutilizável que estamos trabalhando para <strong>integrar a outras partes do Kibana</strong>, como Dashboards, alertas do Kibana e mapas do Kibana.</p><h3>Preenchimento automático inteligente: acelerando a criação de suas consultas.</h3><p>O recurso de autocompletar do ES|QL Editor é abrangente, oferecendo sugestões de funções, argumentos, literais e até mesmo funções aninhadas compatíveis, uma funcionalidade notavelmente ausente no PPL. Na verdade, foi reconstruído do zero, conforme descrito <a href="https://www.elastic.co/search-labs/blog/esql-autocomplete-rebuilt">aqui</a>.</p><p>A validação é executada enquanto o usuário digita, conforme descrito <a href="https://www.elastic.co/search-labs/blog/improving-esql-editor-experience-in-kibana">aqui</a>, e sugerirá campos, além de notificar o usuário sobre erros. Isso reduz a carga mental dos usuários e ajuda a prevenir erros logo no início do processo de criação de consultas.</p><p>Exemplo: Campos e funções compatíveis são sugeridos neste aninhamento:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4d7b2819c34fab31bced7873257b8932b623fba-1502x473.png" style="width: 100%;" /><p>Algo que a PPL não suporta:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f3a89b1255b8a3a762022a2704fdd1c2987e5f9-1013x335.png" style="width: 100%;" /><p>Mesmo com o recurso de autocompletar inteligente guiando você pelas funções, argumentos e funções aninhadas compatíveis, você ainda pode querer uma compreensão mais profunda das opções disponíveis. É exatamente aí que a ajuda contextual do ES|QL Editor se torna indispensável, oferecendo assistência imediata dentro do próprio editor para esclarecer e aprimorar o desenvolvimento de suas consultas.</p><h3>Ajuda contextual ao seu alcance</h3><p>Informações adicionais sobre um comando gerado pelo recurso de autocompletar podem ser acessadas com um clique Ctrl+Espaço. Um painel aparece imediatamente com detalhes sobre a função, o argumento ou o campo em questão. Essa interação simplificada mantém os desenvolvedores focados, fornecendo orientação imediata sem obrigá-los a sair do editor ou a procurar documentação externa. Isso reduz o tempo gasto em pesquisas de sintaxe e ajuda a evitar erros comuns antes que eles ocorram.</p><p>Veja como funciona na prática:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2797f9b5e002dbd83c46475c4ed4dcdc86144a01-1343x522.gif" style="width: 100%;" /><p>O PPL não possui esse nível de orientação integrada, deixando os usuários dependentes de documentação externa ou do método de tentativa e erro. Essa ausência não é apenas uma característica faltante; ela evidencia uma disparidade mais ampla na filosofia de design. ES|QL prioriza uma experiência ponderada e contextualizada que se adapta aos dados e ao fluxo de trabalho do usuário. Essa diferença torna-se mais acentuada à medida que as consultas aumentam em complexidade, fazendo do ES|QL Editor um ambiente mais eficiente e confiável tanto para aprendizado quanto para uso em produção.</p><h3>Consultas recomendadas que levam em consideração o contexto dos dados.</h3><p>O Editor ES|QL fornece consultas recomendadas que são automaticamente adaptadas aos dados com os quais você está trabalhando, como registros. Em vez de apresentar um editor em branco, ele destaca os pontos de partida mais relevantes para casos de uso comuns. Selecionar uma Consulta Recomendada gera uma consulta canônica que pode ser usada imediatamente e refinada conforme necessário. Essa abordagem acelera o desenvolvimento de consultas, especialmente para novos usuários que ainda não conhecem toda a sintaxe.</p><p>Aqui está um exemplo em que um usuário seleciona a consulta “Detectar Ponto de Mudança”:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3284c9340d41298820fbf8c7702abad946b48248-925x370.gif" style="width: 100%;" /><p>Compare isso com a experiência do PPL:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5c3e59dadc6210aede3366bdd081887bcbae7a54-969x798.png" style="width: 100%;" /><p>Em contrapartida, o PPL oferece apenas o preenchimento automático básico, deixando você responsável por montar consultas sem contexto ou estrutura. Essa falta de orientação pode levar à frustração e à tentativa e erro.<br/>Com as Consultas Recomendadas que levam em consideração os dados do Editor ES|QL, você pode evitar começar do zero ou memorizar a sintaxe para tarefas rotineiras. O editor reduz a carga cognitiva, ajuda a prevenir erros e permite que você se concentre na resolução de problemas e em objetivos mais amplos, como executar pesquisas entre clusters, em vez de se preocupar com a construção de consultas.</p><h2>Consultas intuitivas entre clusters</h2><p>O recurso de autocompletar do editor ES|QL continua sendo superior, mesmo ao trabalhar com vários clusters remotos <a href="https://elastic.aiops.work/search-labs/blog/esql-cross-cluster-search">com o CCS</a>. Eis o motivo:</p><h3>O editor ES|QL oferece preenchimento automático contínuo, mesmo em clusters diferentes.</h3><p>O recurso de autocompletar no editor ES|QL suporta não apenas nomes de clusters, mas também<strong> índices locais e remotos</strong>. Conforme explicado <a href="https://www.elastic.co/search-labs/blog/esql-cross-cluster-search">aqui</a>, isso funciona graças a uma arquitetura de nó coordenador, que ajuda a validar e gerar o plano de consulta a ser enviado aos nós locais, executar a consulta e agregar os resultados antes de enviá-los de volta ao usuário. Sem precisar digitar o nome completo do cluster remoto, digitar “:” inicia o processo de autocompletar para o índice remoto. E você não está limitado ao prefixo.</p><p>Isso facilita a descoberta e a consulta em conjuntos de dados distribuídos sem a necessidade de memorizar convenções de nomenclatura ou alternar entre contextos.</p><p>Aqui está um exemplo em que o usuário digita apenas “clu:g” para localizar um índice remoto:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bae1fbacb2320e4d07f41291ea57c9bcf15bf8a5-1092x523.gif" style="width: 100%;" /><p>Em nítido contraste, a PPL fornece apenas preenchimento básico para índices locais, com sugestões restritas a correspondências de prefixos. Os clusters remotos devem ser digitados manualmente, o que aumenta a probabilidade de erros e torna a criação de consultas mais lenta.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e38793c0cc7c6cc7dc0fd4779a3e24ffbb6e0838-1094x263.gif" style="width: 100%;" /><p>O PPL fornece preenchimento automático apenas para índices locais e as sugestões são restritas ao prefixo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5310f824942f94485cace2558ea72c56a0971e22-862x197.png" style="width: 100%;" /><p>O ES|QL vai além, <a href="https://www.elastic.co/docs/solutions/search/cross-cluster-search#exclude-problematic-clusters">permitindo exclusões</a> diretamente usando um sinal negativo, oferecendo controle preciso sobre quais clusters participam da sua exploração. Essa funcionalidade é particularmente valiosa ao trabalhar com ambientes híbridos, onde pode ser necessário incluir ou omitir conjuntos de dados específicos durante investigações entre clusters.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f23ca17f58fbf8e6d27419c028274cb91f30a549-937x78.png" style="width: 100%;" /><p>Essas melhorias refletem o foco mais amplo do Elasticsearch em reduzir o atrito na busca entre clusters. Ao facilitar a construção e o gerenciamento de consultas distribuídas, o ES|QL Editor permite que analistas e desenvolvedores se concentrem em insights em vez de sintaxe, enquanto o PPL deixa grande parte dessa responsabilidade para o usuário. Assim como o ES|QL Editor simplifica a criação de consultas entre clusters, ele também fornece ferramentas para inspecionar como essas consultas são executadas, garantindo transparência e monitoramento de desempenho em vários clusters.</p><h3>Utilizando a ferramenta Inspection para analisar detalhes da pesquisa entre clusters.</h3><p>A ferramenta Inspect, acessível a partir do Editor ES|QL, foi projetada para fornecer metadados com informações explícitas sobre a execução da consulta em todos os clusters. Essa funcionalidade está habilitada no Kibana Discover e pode ser acessada diretamente no inspetor de consultas, permitindo analisar o progresso e os detalhes da pesquisa, o que é particularmente crucial para <strong>a Pesquisa entre Clusters</strong> (<a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-cross-clusters">CCS</a>). Essa funcionalidade ajuda você a monitorar o progresso da pesquisa e a entender o desempenho das consultas em conjuntos de dados distribuídos.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d1c204f70171526fff924c30ea8ad08121a0f8d-919x523.gif" style="width: 100%;" /><p>Essa visibilidade detalhada da execução de consultas, especialmente para pesquisas distribuídas complexas, permite garantir o desempenho ideal e a resolução de problemas.</p><p>Além de compreender a mecânica das consultas individuais, o ES|QL Editor aprimora ainda mais a experiência do usuário, incorporando funcionalidades essenciais em toda a plataforma Kibana, promovendo um fluxo de trabalho contínuo e sem interrupções.</p><h2>Experiência de consulta unificada com ES|QL e Kibana</h2><p>Uma das fontes mais comuns de atrito na análise orientada por consultas é a troca de contexto. Muitas vezes você precisa relembrar perguntas que já escreveu. Cada interrupção quebra o foco e atrasa as investigações. O ES|QL Editor resolve isso integrando o histórico de consultas em todo o Kibana.</p><h3>Consultas recentes</h3><p>O recurso <a href="https://www.elastic.co/search-labs/blog/esql-piped-query-language-goes-ga">Consultas Recentes</a> no Editor ES|QL ajuda você a manter o fluxo de trabalho, tornando o trabalho anterior instantaneamente acessível. No editor ES|QL do Discover, você pode visualizar, executar novamente e marcar com estrela suas últimas 20 consultas, garantindo que as consultas mais usadas ou complexas estejam a apenas um clique de distância. Essas consultas salvas também são transferidas para o Kibana, integrando-se a painéis, visualizações, alertas e mapas, para que você não precise sair da tela atual nem digitar os comandos novamente. Isso reduz o trabalho repetitivo, acelera as investigações e minimiza o risco de erros.</p><p>Por exemplo, um usuário pode utilizar as Consultas Recentes no Editor ES|QL do Discover (e marcá-las com uma estrela):</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb0f9fe37b92dec421c394d31ae7d90afebe062e-1421x793.png" style="width: 100%;" /><p>As consultas recentes estão integradas no painel de controle:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b84cd3a9bdec58812360d2aba4fc7713363ee3cc-1411x797.png" style="width: 100%;" /><p>O PPL não oferece nenhuma funcionalidade comparável, obrigando os usuários a recorrerem à cópia e colagem manual ou a anotações externas para reutilizar consultas. A diferença vai além da conveniência; ela reflete a estratégia da Elastic de construir o ES|QL como uma linguagem verdadeiramente integrada ao ecossistema Kibana. Com recursos como Consultas Recentes, o ES|QL Editor não apenas simplifica os fluxos de trabalho diários, mas também estabelece as bases para funcionalidades mais avançadas, agora em versão prévia técnica, garantindo que a experiência continue a evoluir.</p><h2>Conclusão</h2><p>ES|QL é mais do que uma sintaxe; reflete a estratégia da Elastic para melhorar a forma como os usuários pesquisam, exploram e analisam dados. Com autocompletar inteligente, consultas recomendadas sensíveis ao contexto, orientações integradas ao editor e ferramentas como o Inspect, o ES|QL Editor acelera o aprendizado, reduz erros e simplifica fluxos de trabalho complexos, como a análise entre clusters. Integrado ao Kibana, ele conecta consultas perfeitamente a painéis, alertas e visualizações, garantindo um fluxo de trabalho ininterrupto.</p><p>Em resumo, ES|QL não é apenas mais uma linguagem de encaminhamento; é um mecanismo de consulta cuidadosamente projetado, aliado a uma interface de usuário intuitiva, que redefine fundamentalmente a forma como você interage com seus dados, oferecendo uma experiência integrada, inteligente e em constante evolução, que contrasta fortemente com a natureza frequentemente sequencial e menos guiada do OpenSearch PPL.</p><h2>O que vem a seguir?</h2><p>Este blog apenas aborda superficialmente o ES|QL. As próximas publicações aprofundarão as comparações com o OpenSearch PPL e explorarão recursos geoespaciais, de visualização e funcionalidades futuras do editor, como <a href="https://www.elastic.co/docs/explore-analyze/dashboards/add-controls">Controles</a> (já disponíveis em Painéis), guias de exploração de múltiplos dados, pesquisa em segundo plano, histórico de consultas mais completo e FUSE.</p><h2>Experimente o ES|QL hoje mesmo</h2><p>Você pode experimentar o ES|QL em projetos Elasticsearch <a href="https://www.elastic.co/cloud/serverless">Serverless</a> totalmente gerenciados com um <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/create-serverless-project">período de avaliação gratuito</a>. Também está disponível em versões a partir da 8.11, mas a melhor experiência é obtida nas <a href="https://www.elastic.co/blog/whats-new-elastic-9-1-0">versões 8.19 e 9.1</a>.</p><p>Comece em minutos no seu ambiente local com um único comando:</p><pre><code>curl -fsSL https://elastic.co/start-local | sh</code></pre>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apresentamos o construtor de consultas ES|QL para o cliente Ruby do Elasticsearch.]]></title>
            <description><![CDATA[Aprenda a usar o construtor de consultas ES|QL recém-lançado para o cliente Ruby do Elasticsearch. Uma ferramenta para criar consultas ES|QL mais facilmente com código Ruby.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/esql-query-builder-elasticsearch-ruby-client</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/esql-query-builder-elasticsearch-ruby-client</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Fernando Briano]]></dc:creator>
            <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f8e1263ab53d356824a4fc539084151be80899db-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Recentemente lançamos <a href="https://github.com/elastic/esql-ruby/"><code>elastic-esql</code></a>, uma gem Ruby publicada sob a licença Apache 2. Esta gem permite que você crie consultas <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql">ES|QL</a> da Elastic em Ruby idiomático, que você pode então usar com a API de consulta ES|QL. O ES|QL permite que os desenvolvedores filtrem, transformem e analisem dados armazenados no Elasticsearch por meio de consultas. Ele usa &quot;pipes&quot; ( <code>|</code> ) para trabalhar com os dados passo a passo. A gem usa funções Ruby, que você pode encadear ao objeto original para construir consultas mais complexas:</p><p><strong>ESQL:</strong></p><pre><code>FROM sample_data | LIMIT 2 | SORT @timestamp DESC</code></pre><p><strong>Rubi:</strong></p><pre><code>Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending</code></pre><h2>Instalação</h2><p>A gem pode ser instalada a partir do RubyGems com o seguinte comando:</p><pre><code>gem install elastic-esql</code></pre><p>Ou pode ser adicionado ao Gemfile de um projeto:</p><pre><code>gem 'elastic-esql'</code></pre><h2>Uso</h2><p>Você pode construir uma consulta completa de uma só vez ou criar um objeto de consulta com um comando de origem como <code>from</code> ou <code>row</code> e, em seguida, encadear métodos ES|QL para construí-lo.</p><pre><code>query = Elastic::ESQL.from('sample_data')
query.limit(2).sort('@timestamp')</code></pre><p>A gem traduz o código para ES|QL no método <code>to_s</code> , portanto, retorna a consulta ES|QL quando é impressa ou convertida em uma String:</p><pre><code>query = Elastic::ESQL.from('sample_data').limit(2).sort('@timestamp').descending
query.to_s
# => "FROM sample_data | LIMIT 2 | SORT @timestamp DESC"</code></pre><p>Você pode instanciar um objeto de consulta e modificar seu estado inicial usando os equivalentes <code>!</code> de cada função:</p><pre><code>query = Elastic::ESQL.from('sample_data')
query.to_s
# => "FROM sample_data"
query.limit!(2).sort!('@timestamp')
query.to_s
# => "FROM sample_data | LIMIT 2 | SORT @timestamp"</code></pre><p>A ferramenta fornece maneiras convenientes de encadear etapas extras a uma função ES|QL, como <code>enrich</code> e <code>sort</code>. Depois de chamar <code>enrich</code> em um objeto <code>Elastic::ESQL</code> , você pode encadear <code>on</code> e <code>with</code> a ele:</p><pre><code>esql.enrich!('policy').on('a').with({ name: 'language_name' })</code></pre><p>Você também pode encadear <code>desc</code>, <code>asc</code>, <code>nulls_first</code> e <code>nulls_last</code> à sua consulta após usar <code>sort</code>:</p><pre><code>Elastic::ESQL.from('sample_data').sort('@timestamp').asc.to_s
# => 'FROM sample_data | SORT @timestamp ASC'

Elastic::ESQL.from('sample_data').sort('@timestamp').desc.nulls_first.to_s
# => 'FROM sample_data | SORT @timestamp DESC NULLS FIRST'</code></pre><p>Também oferece suporte a strings personalizadas, caso você queira escrever a consulta ES|QL por conta própria ou usar um recurso que ainda não foi adicionado à biblioteca. <code>custom</code> irá unir as strings no final da consulta. Isso os adicionará conforme forem enviados para a função, sem adicionar nenhum caractere de barra vertical. Eles serão combinados ao restante da consulta por um caractere de espaço.</p><pre><code>esql = Elastic::ESQL.from('sample_data')
esql.custom('| MY_VALUE = "test value"').to_s
# => 'FROM sample_data | MY_VALUE = "test value"'</code></pre><p>Você também pode encadear funções <code>custom</code> :</p><pre><code>esql.custom('| MY_VALUE = "test value"').custom('| ANOTHER, VALUE')
'FROM sample_data | MY_VALUE = "test value" | ANOTHER, VALUE'</code></pre><h2>Utilizando o Construtor de Consultas ES|QL com o cliente Ruby</h2><p>Você pode usar o construtor de consultas diretamente com <a href="https://github.com/elastic/elasticsearch-ruby">elasticsearch-ruby</a> e a API <code>esql.query</code> enviando o objeto de consulta:</p><pre><code>require 'elasticsearch'
require 'elastic/esql'

client = Elasticsearch::Client.new
index = 'sample_data'

query = Elastic::ESQL.from(index)
                     .sort('@timestamp')
                     .desc
                     .where('event_duration > 5000000')
                     .limit(3)
                     .eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })
client.esql.query(body: { query: query })</code></pre><p>Você também pode usá-lo com o auxiliar ES|QL do cliente Ruby do Elasticsearch. <a href="https://www.elastic.co/search-labs/blog/esql-ruby-helper-elasticsearch">Saiba mais</a>:</p><pre><code>require 'elasticsearch/helpers/esql_helper'

Elasticsearch::Helpers::ESQLHelper.query(client, query)</code></pre><h2>Como uma ferramenta independente</h2><p>A gem foi projetada como uma ferramenta independente para construir consultas ES|QL de forma idiomática. Não possui dependências de tempo de execução; você pode usá-lo com o cliente oficial do Elasticsearch para Ruby ou de forma independente.</p><p>A consulta gerada pode ser usada com a API <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-esql-query"><code>esql.query</code></a> de qualquer forma que um aplicativo interaja com a API do Elasticsearch (Ruby ou não). Uma vez que uma consulta é construída com <code>elastic-esql</code>, a String gerada pode ser enviada para a API como o parâmetro <code>query</code> no corpo da solicitação. </p><p>Anteriormente, escrevi sobre <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ruby-tools">como usar o Elasticsearch com ferramentas populares do Ruby</a>. Esta gem pode ser usada com qualquer uma das ferramentas populares do Ruby para consultar o Elasticsearch com ES|QL.</p><h2>Conclusão</h2><p>Esta biblioteca está em desenvolvimento ativo e a API final ainda não foi concluída. Atualmente, está disponível em versão de pré-visualização técnica. Se você tiver algum comentário sobre a API atual ou sobre o uso em geral, não hesite em <a href="https://github.com/elastic/esql-ruby/issues">abrir uma nova solicitação</a>. Consulte <a href="https://github.com/elastic/esql-ruby/?tab=readme-ov-file#ruby-esql-query-builder">o arquivo README</a> para saber mais sobre o construtor de consultas Ruby ES|QL.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/vector-search-filtering</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/vector-search-filtering</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Carlos Delgado]]></dc:creator>
            <pubDate>Wed, 03 Sep 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03b1dd4c7bda4fbabd8e374bc2e4f12d5be6ef5f-1600x1150.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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>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.</li><li>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.</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><pre><code>{
  "query": {
    "bool": {
      "must": {
        "knn": {
          "field": "image-vector",
          "query_vector": [54, 10, -2],
          "k": 5,
          "num_candidates": 50
        }
      },
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}</code></pre><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><pre><code>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, 2],
    "k": 5,
    "num_candidates": 50
  },
  "post_filter": {
    "term": {
      "file-type": "png"
    }
  }
}</code></pre><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><pre><code>{
  "knn": {
    "field": "image-vector",
    "query_vector": [54, 10, -2],
    "k": 5,
    "num_candidates": 50,
    "filter": {
      "term": {
        "file-type": "png"
      }
    }
  }
}</code></pre><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><pre><code>{
  "query": {
    "knn": {
      "field": "image-vector",
      "query_vector": [-5, 9, -12],
      "k": 5,
      "filter": {
        "term": {
          "file-type": "png"
        }
      }
    }
  }
}</code></pre><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:&#x27;2025-05-28T01:27:58.456Z&#x27;,to:&#x27;2025-06-30T13:53:26.430Z&#x27;))&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://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3eac3748a33376fc97d957364a5c1f5108d5c58b-1023x896.png" style="width: 100%;" /><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>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.</li><li>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.</li><li>A pós-filtragem não recupera necessariamente os k melhores resultados, pois eles podem ser filtrados pelo filtro quando este é aplicado.</li></ul><p>Boa filtragem!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fragmentos e réplicas do Elasticsearch: um guia prático]]></title>
            <description><![CDATA[Domine os conceitos de shards e réplicas do Elasticsearch e aprenda como otimizá-los.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-shards-and-replicas-guide</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-shards-and-replicas-guide</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Piotr Przybyl]]></dc:creator>
            <pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7775c44f01f2516c4ff4cce6d6bbe9e7b2c38908-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Elasticsearch potencializa o Lucene ao construir um sistema distribuído sobre ele, o que resolve os problemas de escalabilidade e tolerância a falhas. Também disponibiliza uma API REST baseada em JSON, tornando a interoperabilidade com outros sistemas muito simples.</p><p>Sistemas distribuídos como o Elasticsearch podem ser muito complexos, com muitos fatores que podem afetar seu desempenho e estabilidade. <strong>Os shards</strong> estão entre os conceitos mais fundamentais do Elasticsearch, e entender como eles funcionam permitirá que você gerencie um cluster Elasticsearch de forma eficaz.</p><p>Este artigo explica o que são shards primários e réplicas, seu impacto em um cluster Elasticsearch e quais ferramentas existem para ajustá-los a diferentes demandas.</p><h2>Entendendo os fragmentos</h2><p>Os dados em um índice Elasticsearch podem crescer a proporções gigantescas. Para manter a organização, cada dado é armazenado em um índice, e os índices são divididos em vários <strong>fragmentos</strong>. Cada fragmento do Elasticsearch é um índice Apache Lucene, sendo que cada índice Lucene individual contém um subconjunto dos documentos presentes no índice do Elasticsearch. Dividir os índices dessa forma mantém o uso de recursos sob controle. Um índice Apache Lucene tem um limite de 2.147.483.519 (2³¹ - 129) documentos.</p><p>Por vezes, os índices precisam ser movidos entre nós para fins de rebalanceamento. Como esse processo pode ser demorado e exigir muitos recursos, os índices não devem crescer demais, o que ajuda a manter o tempo de recuperação em níveis gerenciáveis. Além disso, como os índices são compostos por segmentos do Lucene que precisam ser constantemente mesclados, é importante que os segmentos não fiquem muito grandes. Por esses motivos, o Elasticsearch divide os dados do índice em partes menores e mais gerenciáveis, chamadas de <strong>shards primários</strong>, que podem ser distribuídas mais facilmente por várias máquinas. Os fragmentos <strong>de réplica</strong> são simplesmente uma cópia exata de um fragmento primário correspondente, e abordaremos sua função mais adiante neste artigo.</p><p>Ter o número correto de shards é importante para o desempenho. Portanto, é sensato planejar com antecedência. Quando as consultas são executadas em paralelo em diferentes shards, elas são executadas mais rapidamente do que um índice composto por um único shard, mas somente se cada shard estiver localizado em um nó diferente e houver nós suficientes no cluster. Ao mesmo tempo, porém, os shards consomem memória e espaço em disco, tanto em termos de dados indexados quanto de metadados do cluster. Ter muitos shards (também conhecido como sobresharding) pode tornar as consultas, as solicitações de indexação e as operações de gerenciamento mais lentas, sendo, portanto, fundamental manter o equilíbrio certo.</p><p>O número de shards primários é definido no momento da criação do índice <strong>para aquela instância de índice específica</strong>. Se precisar de um número diferente de shards primários posteriormente, você pode usar as<strong> APIs de redimensionamento</strong> : <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-split">split</a> (mais shards primários), <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-shrink">shrink</a> (menos shards primários) ou <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-indices-clone">clone</a> (o mesmo número de shards primários com novas configurações para réplicas). Essas operações copiam segmentos do Lucene e <strong>evitam uma reindexação completa de todos os documentos</strong>. Ao criar um índice, você pode definir o número de shards primários e de réplicas nas configurações do índice:</p><pre><code>PUT /sensor
{
   "settings" : {
       "index" : {
           "number_of_shards" : 6,
           "number_of_replicas" : 2
       }
   }
}</code></pre><p>(Caso não especifique o número de shards ou réplicas, o valor padrão para ambos é 1, a partir do Elasticsearch 7.0). O número ideal de fragmentos deve ser determinado com base na quantidade de dados em um índice. Em geral, <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards">um shard ideal deve conter de 10 a 50 GB de dados</a>, com menos de 200 milhões de documentos por shard. Por exemplo, se você espera acumular cerca de 300 GB de logs de aplicativos por dia, ter cerca de 10 shards nesse índice seria razoável, desde que você tenha nós suficientes para hospedá-los.</p><p>Durante sua existência, os fragmentos podem passar por diversos estados, incluindo:</p><ul><li><strong>Inicialização:</strong> Estado inicial antes que o fragmento possa ser usado.</li><li><strong>Iniciado:</strong> Estado em que o fragmento está ativo e pode receber solicitações.</li><li><strong>Relocação:</strong> Estado que ocorre quando os fragmentos estão em processo de serem movidos para um nó diferente. Isso pode ser necessário em certas condições, por exemplo, quando o nó em que estão instalados está ficando sem espaço em disco.</li><li><strong>Não atribuído:</strong> O estado de um fragmento que não pôde ser atribuído. Quando isso acontece, é apresentada uma justificativa, por exemplo, se o nó que hospeda o shard não estiver mais no cluster <em>(NODE_LEFT)</em> ou devido à restauração em um índice fechado <em>(EXISTING_INDEX_RESTORED).</em></li></ul><p>Para visualizar todos os fragmentos (shards), seus estados e outros metadados, você pode usar a seguinte solicitação:</p><pre><code>GET _cat/shards</code></pre><p>Para visualizar os fragmentos de um índice específico, você pode adicionar o nome do índice à URL, por exemplo, sensor:</p><pre><code>GET _cat/shards/sensor</code></pre><p>Este comando produz uma saída, como no exemplo a seguir. Por padrão, as colunas exibidas incluem o nome do índice, o nome (ou seja, número) do fragmento, se é um fragmento primário ou uma réplica, seu estado, o número de documentos, o tamanho em disco, bem como o endereço IP e o ID do nó onde o fragmento está localizado.</p><pre><code>sensor 5 p STARTED    0  283b 127.0.0.1 ziap
sensor 5 r UNASSIGNED                  
sensor 2 p STARTED    1 3.7kb 127.0.0.1 ziap
sensor 2 r UNASSIGNED                  
sensor 3 p STARTED    3 7.2kb 127.0.0.1 ziap
sensor 3 r UNASSIGNED                  
sensor 1 p STARTED    1 3.7kb 127.0.0.1 ziap
sensor 1 r UNASSIGNED                  
sensor 4 p STARTED    2 3.8kb 127.0.0.1 ziap
sensor 4 r UNASSIGNED                  
sensor 0 p STARTED    0  283b 127.0.0.1 ziap
sensor 0 r UNASSIGNED</code></pre><h2>Entendendo as réplicas</h2><p>Embora cada fragmento contenha uma única cópia dos dados, um índice pode conter várias cópias do fragmento. Existem, portanto, dois tipos de fragmentos: o <strong>fragmento primário</strong> e uma cópia, ou <strong>réplica</strong>. Cada réplica de um shard primário está sempre localizada em um nó diferente, o que garante alta disponibilidade dos seus dados em caso de falha de um nó. Além da redundância e de seu papel na prevenção de perda de dados e tempo de inatividade, as réplicas também podem ajudar a melhorar o desempenho da pesquisa, permitindo que as consultas sejam processadas em paralelo com o shard primário e, portanto, mais rapidamente.</p><p>Existem algumas diferenças importantes no comportamento dos fragmentos primários e das réplicas. Embora ambos sejam capazes de processar consultas, solicitações de indexação (ou seja, A adição de dados ao índice deve primeiro passar pelos shards primários antes de poder ser replicada para os shards de réplica. Conforme mencionado acima, se um shard primário ficar indisponível — por exemplo, devido à desconexão de um nó ou falha de hardware — uma réplica é promovida para assumir sua função.</p><p>Embora as réplicas possam ajudar em caso de falha de um nó, é importante não ter muitas delas, pois consomem memória, espaço em disco e poder computacional durante a indexação. Outra diferença entre os shards primários e as réplicas é que, enquanto o número de shards primários não pode ser alterado após a criação do índice, o número de réplicas pode ser alterado dinamicamente a qualquer momento, atualizando as configurações do índice.</p><p>Outro fator a ser considerado com réplicas é o número de nós disponíveis. As réplicas são sempre colocadas em nós diferentes do shard primário, uma vez que duas cópias dos mesmos dados no mesmo nó não ofereceriam proteção caso o nó falhasse. Consequentemente, para que um sistema suporte <em>n</em> réplicas, é necessário que haja pelo menos <em>n + 1</em> nós no cluster. Por exemplo, se houver dois nós em um cluster e um índice estiver configurado com seis réplicas, apenas uma réplica será alocada. Por outro lado, um sistema com sete nós é perfeitamente capaz de lidar com um shard primário e seis réplicas.</p><h2>Otimizando fragmentos e réplicas</h2><p>Mesmo após a criação de um índice com o equilíbrio correto entre shards primários e réplicas, é necessário monitorá-lo, pois a dinâmica em torno de um índice muda ao longo do tempo. Por exemplo, ao lidar com dados de séries temporais, os índices com dados recentes são geralmente mais ativos do que os mais antigos. Sem ajustar esses índices, todos eles consumiriam a mesma quantidade de recursos, apesar de suas necessidades serem muito diferentes.</p><p>A API de índice de rollover pode ser usada para separar índices mais recentes de índices mais antigos. É possível configurá-lo para criar automaticamente um novo índice quando um determinado limite — como o tamanho do índice no disco, o número de documentos ou sua idade — for atingido. Essa API também é útil para manter o tamanho dos fragmentos sob controle. Como o número de fragmentos não pode ser facilmente alterado após a criação do índice, os fragmentos continuarão acumulando dados se nenhuma condição de rollover for atendida. Para índices mais antigos que exigem acesso pouco frequente, reduzir o tamanho e forçar a fusão de um índice são duas maneiras diferentes de diminuir o espaço ocupado na memória e no disco. O primeiro reduz o número de fragmentos em um índice, enquanto o segundo reduz o número de segmentos do Lucene e libera espaço usado por documentos que foram excluídos.</p><h2>Fragmentos primários e réplicas como base do Elasticsearch</h2><p>O Elasticsearch construiu uma sólida reputação como plataforma distribuída de armazenamento, busca e análise para grandes volumes de dados. Ao operar em tal escala, porém, desafios inevitavelmente surgirão. Por isso, entender como funcionam os shards primários e de réplica é tão importante e fundamental para o Elasticsearch, pois isso pode ajudar a otimizar a confiabilidade e o desempenho da plataforma.</p><p>Saber como funcionam e como otimizá-los é fundamental para obter um cluster Elasticsearch mais robusto e com melhor desempenho. Se você está enfrentando lentidão nas respostas às consultas ou interrupções frequentes, esse conhecimento pode ser a chave para superar esses obstáculos.</p><p>Siga a documentação oficial do Elasticsearch para saber mais sobre <a href="https://www.elastic.co/docs/deploy-manage/distributed-architecture/clusters-nodes-shards">clusters, nós e shards</a>, <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards">como dimensionar seus shards</a>, <a href="https://www.elastic.co/docs/deploy-manage/distributed-architecture/shard-allocation-relocation-recovery">alocação de shards e recuperação</a>.</p><p>Este tópico também está disponível como um curso introdutório no <a href="https://youtu.be/sAySPSyL2qE">canal da comunidade Elastic no YouTube.</a></p><p>Por último, mas não menos importante: se você não quiser se preocupar com nós, shards ou réplicas, pode experimentar o <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless">Elastic Cloud Serverless</a>. Esta oferta da Elastic Cloud é totalmente gerenciada pela Elastic e automatizada para escalar de acordo com sua carga de trabalho. Um período de teste gratuito pode ajudá-lo a se familiarizar com outros benefícios da abordagem sem servidor.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como exibir os campos de um índice do Elasticsearch]]></title>
            <description><![CDATA[Aprenda como exibir os campos de um índice do Elasticsearch usando as APIs _mapping e _search, subcampos, _source sintético e campos de tempo de execução.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-index-show-fields</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-index-show-fields</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Wed, 06 Aug 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, discutiremos como exibir os campos de um índice do Elasticsearch. Isso pode ser útil para entender a estrutura dos seus dados, identificar campos específicos e solucionar problemas. Abordaremos os seguintes tópicos:</p><ol><li>Utilizando a API <code>_mapping</code> para recuperar informações de campo</li><li>Utilizando a API <code>_search</code> para exibir valores de campo</li><li>Exibição de subcampos</li><li>_source sintética</li><li>Campos de tempo de execução</li></ol><h2>1. Utilizando a API _mapping para recuperar informações de campo</h2><p>A API <code>_mapping</code> permite recuperar a definição de mapeamento para um índice ou vários índices. Isso inclui informações sobre os campos, seus tipos de dados e outras propriedades. Para recuperar o mapeamento de um índice específico, utilize a seguinte solicitação:</p><pre><code>GET /<index_name>/_mapping</code></pre><p>Por exemplo, se você tiver um índice chamado <code>my_index</code>, poderá recuperar seu mapeamento com a seguinte solicitação:</p><pre><code>GET /my_index/_mapping</code></pre><p>A resposta incluirá a definição de mapeamento para o índice, que contém informações sobre os campos e suas propriedades.</p><p>Também é possível recuperar o mapeamento de um campo específico. Isso pode ser útil se o seu mapeamento for muito extenso e você quiser se concentrar apenas em um campo específico. Para obter o mapeamento de um campo específico, utilize a seguinte solicitação:</p><pre><code>GET /my_index/_mapping/field/my_field</code></pre><p>Você também pode recuperar os mapeamentos de vários campos separando seus nomes por vírgulas, como na seguinte solicitação:</p><pre><code>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3</code></pre><h2>2. Usando a API _search para exibir valores de campo</h2><p>Para exibir os valores dos campos em um índice do Elasticsearch, você pode usar a API <code>_search</code> . A API <code>_search</code> oferece várias maneiras de controlar quais campos são retornados; as duas principais são:</p><ol><li><strong><code>_source</code></strong>O campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field"><code>_source</code></a> contém o corpo original do documento JSON exatamente como foi indexado, incluindo quaisquer alterações feitas pelos pipelines de ingestão ou etapas de pré-processamento. Para exibir campos específicos do documento de origem, implemente a filtragem de origem, como veremos a seguir.</li><li><strong><code>fields</code></strong>O parâmetro <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields"><code>fields</code></a> permite recuperar campos específicos dos seus documentos ao realizar uma pesquisa, com base no mapeamento do índice. Ao contrário de <code>_source</code>, <code>fields</code> também pode retornar valores de campos armazenados, valores de documentos ou campos de tempo de execução sem fazer referência a <code>_source</code>, embora para campos padrão sem valores de documentos ou configurações armazenadas, ele recorra a <code>_source</code>. Isso pode trazer muitos benefícios, como melhoria de desempenho e outros, como veremos a seguir.</li></ol><h3>Usando o campo _source<code> </code></h3><p>Por padrão, a API<code> _search</code> retorna o campo <code>_source</code> , que contém o documento JSON original que foi indexado. Para exibir campos específicos, você pode adicionar filtros no parâmetro <code>_source </code>da solicitação de pesquisa; isso é chamado de filtragem de origem.</p><p>Aqui está um exemplo de uma solicitação de pesquisa que retorna os valores dos campos <code>title </code>e <code>author</code> para documentos no índice <code>my_index</code> :</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}</code></pre><p>Neste exemplo, o parâmetro <code>_source</code> especifica os campos a serem retornados.</p><p>Se você precisar de ainda mais controle, pode usar as propriedades <code>includes</code> e <code>excludes </code>do objeto <code>_source</code> . Por exemplo, a consulta abaixo retorna o campo de nível superior <code>title</code> e todos os subcampos de <code>author</code> exceto <code>author.description</code>.</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
     “includes”: [“title”, “author.*],
     “excludes”: [“author.description”]
  }
}</code></pre><p>Neste exemplo, usamos o padrão <code>author.* </code>para recuperar todos os subcampos diretos do objeto <code>author </code> . Então excluímos explicitamente <code>author.description </code>para que apenas os outros campos de autor sejam retornados. Note que isso não traz nenhuma melhoria de desempenho, já que ainda precisa carregar e analisar o JSON de origem, mas pode reduzir o tamanho da resposta enviada pela rede.</p><h3>Usando o parâmetro de campos</h3><p>Você pode usar o parâmetro <code>fields</code> para filtrar os campos retornados na resposta da pesquisa. O uso de <code>fields</code> em vez de <code>_source</code> oferece diversas vantagens, incluindo:</p><ul><li><strong>Desempenho aprimorado: </strong><code>fields </code>pode retornar valores diretamente de <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">campos armazenados</a> ou <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/doc-values">valores de documentos</a> sem ter que carregar o <code>_source</code> completo, tornando o tamanho da carga útil da resposta menor.</li><li><strong>Saída formatada:</strong> Para campos padrão,<code> fields</code> pode recorrer a <code>_source</code> para obter os valores, mas ele analisa o mapeamento do índice para formatar corretamente a saída, como datas formatadas, tornando-as consistentes com o que é usado para agregações e classificação.</li><li><strong>Acesso a campos de tempo de execução:</strong> <code>fields</code> pode retornar campos de tempo de execução, que não existem no <code>_source</code> original.</li><li>Você pode encontrar mais benefícios <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#search-fields-param">aqui</a>.</li></ul><p>Por exemplo, para retornar apenas os campos <code>title</code> e <code>author</code> no índice <code>my_index</code> , você pode usar a seguinte solicitação de pesquisa:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}</code></pre><p>Na consulta acima, definimos o campo <code>_source </code>como falso para não retornarmos o documento de origem. Isso pode minimizar drasticamente o tamanho da carga útil da resposta, mas lembre-se de que isso só funciona porque os campos <code>title</code> e <code>author</code> são do tipo de campo <code>keyword </code> , que têm <code>doc_values</code> habilitado por padrão. Se o campo não tiver <code>doc_values</code> habilitado e <code>_source</code> estiver definido como falso, o Elasticsearch não terá como recuperá-los e eles serão ignorados na resposta.</p><p>É importante notar que a resposta <code>fields</code> sempre retorna uma matriz de valores para cada campo, mesmo que haja apenas um único valor. Isso ocorre porque o Elasticsearch não possui um tipo de array dedicado, e qualquer campo pode ter vários valores. Para obter mais informações sobre arrays no Elasticsearch, clique <a href="http://elastic.co/docs/reference/elasticsearch/mapping-reference/array">aqui</a>.</p><h3>Outras formas de recuperar campos</h3><p>Embora a recuperação de campos usando <code>_source</code> ou <code>fields</code> sejam os métodos recomendados, existem outros métodos disponíveis para casos de uso específicos, como:</p><p><strong>Campos de valor do documento:</strong> Se você quiser evitar <code>_source</code> completamente, você pode pesquisar usando o parâmetro <code><a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#docvalue-fields">docvalue_fields</a> </code> . Os valores do documento armazenam os mesmos valores de campo que <code>_source</code> , mas em uma estrutura de dados em disco, otimizada para classificação e agregações.</p><p>Como é separado dos valores armazenados com <code>_source</code>, você pode solicitar campos específicos sem carregar todo o <code>_source</code>. Isso é útil se você estiver consultando documentos grandes, mas precisar apenas de alguns campos pequenos que suportem valores do tipo &quot;doc&quot;. Outro caso de uso para usar <code>docvalue_fields </code>é quando você deseja usar formatação personalizada nos campos <code>date</code> e <code>numeric</code> , como veremos no exemplo abaixo.</p><p>Observe que isso só funciona para campos que você habilita <code>doc_values</code> ou para tipos de campo que o têm habilitado por padrão, como <code>keyword</code>, <code>date</code>, tipos numéricos e <code>boolean</code>, não para <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text"><code>text</code></a> ou <a href="https://www.elastic.co/docs/reference/elasticsearch/plugins/mapper-annotated-text-usage"><code>annotated_text</code></a>.</p><p>Neste exemplo, usamos o parâmetro <code>docvalue_fields</code> para recuperar os campos <code>title</code>, <code>author</code> e <code>published</code> sem carregar o documento <code>_source</code> completo:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "title",
    "author",
    {
      "field": "published",
      "format": "epoch_millis"
    }
  ],
  "_source": false
}</code></pre><p>Quando esta consulta é executada, o Elasticsearch obtém os valores diretamente de seu armazenamento colunar em disco, em vez de referenciar o <code>_source </code>para cada documento. O campo <code>published</code> é retornado com o formato <code>epoch_millis</code> em vez do formato padrão, graças ao parâmetro <code>format</code> fornecido na consulta.</p><p><strong>Campos armazenados:</strong> Se você marcou explicitamente campos específicos como <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">armazenados</a> no mapeamento, você pode usar o parâmetro <code>stored_fields</code> para filtrar esses campos. Isso é útil se você deseja respostas resumidas apenas com esses campos específicos ou para campos que você armazenou deliberadamente para recuperação posterior. É armazenado separadamente de <code>_source</code>, portanto, este método também é útil para evitar a necessidade de carregar <code>_source</code>.</p><p>É importante notar que esta opção está desativada por padrão e geralmente não é recomendada. Em vez disso, utilize a filtragem de origem para retornar determinados subconjuntos do documento de origem original.</p><p>Na consulta de exemplo abaixo, usamos o parâmetro <code>stored_fields</code> para recuperar o campo <code>summary</code> , que tem a configuração de mapeamento de índice de ”<code>store”: true</code>.</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "stored_fields": ["summary"]
}</code></pre><p>Quando esta consulta é executada, o Elasticsearch verifica se este campo foi marcado com <code>”store”: true</code>, se não o encontrar, irá ignorar o campo completamente.</p><h2>3. Exibição de subcampos</h2><p>Se o seu índice contiver subcampos, você pode usar a notação de ponto para especificar o caminho do campo no parâmetro <code>fields</code> . Note que os subcampos são diferentes do <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested">tipo de campo aninhado</a>. Por exemplo, se você tiver um subcampo chamado <code>address.city</code>, poderá incluí-lo na resposta da pesquisa desta forma:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}</code></pre><p>Neste exemplo, a resposta da pesquisa incluirá os valores dos campos <code>title</code>, <code>author</code> e <code>address.city</code> .</p><h2>4. Fonte sintética</h2><p>Se você quiser manter a funcionalidade de usar<code> _source</code> , mas também economizar espaço em disco, você tem a opção de usar <code>_source</code> sintético em seu mapeamento de índice. <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source"><code>_source</code></a> <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source">sintético </a>é um recurso que permite ao Elasticsearch reconstruir o <code>_source</code> a partir de dados existentes, como campos armazenados e valores de documentos, mesmo quando <code>_source</code> está desativado. Isso permite economizar bastante espaço de armazenamento, ao custo de velocidades ligeiramente menores no momento da consulta, já que a reconstrução ocorre em tempo real. Ative este recurso usando os valores abaixo nas configurações do seu índice:</p><pre><code>PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}</code></pre><p>Algumas vantagens de usar <code>_source </code>sintético incluem: exibição do documento completo ao usar a API <code>_search</code> , filtragem de origem e compatibilidade com outros recursos e ferramentas como o Kibana que esperam que <code>_source</code> esteja disponível, tudo isso evitando a necessidade de armazenar o documento <code>_source</code> completo.</p><h2>5. Campos de tempo de execução</h2><p><a href="https://www.elastic.co/docs/manage-data/data-store/mapping/runtime-fields">Os campos de tempo de execução</a> permitem definir campos com script no momento da consulta ou no mapeamento do índice, dentro de um bloco de tempo de execução. Esses campos nunca são indexados, portanto, adicionar um campo de tempo de execução não aumenta o tamanho do índice, mas nunca aparecerá em <code>_source</code>. Os campos de tempo de execução definidos no mapeamento são persistentes e estão disponíveis para todas as consultas, enquanto os campos de tempo de execução definidos no momento da consulta são temporários e estão disponíveis apenas nessa solicitação de pesquisa.</p><p>A principal vantagem de usar campos em tempo de execução é a capacidade de adicionar campos aos documentos depois de já os ter importado, simplificando as decisões de mapeamento. Os campos de tempo de execução também são ótimos para enriquecer seus documentos com valores que não existem no documento original, mas são gerados por meio de um script, como formatar uma string ou calcular uma pontuação.</p><p>Vale ressaltar também que os campos de tempo de execução podem prejudicar o desempenho, pois será necessário executar um script para cada documento no conjunto de resultados. Para <a href="https://www.elastic.co/docs/manage-data/data-store/mapping/retrieve-runtime-field">recuperar um campo de tempo de execução</a>, você também pode usar o parâmetro <code>fields</code> na API <code>_search</code> .</p><h2>Conclusão</h2><p>A exibição de campos de um índice Elasticsearch pode variar desde a simples recuperação de valores usando o mapeamento de índice ou o <code>_source</code>, até métodos mais avançados usando <code>fields</code>, <code>docvalue_fields</code> ou campos de tempo de execução para maior controle e eficiência. Compreender as vantagens e desvantagens de diferentes métodos é fundamental para otimizar suas experiências de busca. Seja para otimizar payloads, enriquecer documentos ou usar dados sintéticos <code>_source</code> para economizar armazenamento, o Elasticsearch oferece diversas ferramentas e recursos para encontrar os dados que você precisa, da maneira que você precisa. Essas técnicas podem ajudá-lo a entender a estrutura de seus dados, identificar campos específicos e solucionar problemas.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Painéis de controle com inteligência artificial: da visão ao Kibana]]></title>
            <description><![CDATA[Gere um painel de controle usando um LLM para processar uma imagem e transformá-la em um painel do Kibana.
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ai-powered-dashboards</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ai-powered-dashboards</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo, Tomás Murúa]]></dc:creator>
            <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb60ceb2fbc3941745b21ae3357cbb6ea8fab18c-1443x811.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><a href="https://www.elastic.co/kibana/kibana-lens">O Kibana Lens</a> torna o arrastar e soltar de dashboards muito simples, mas quando você precisa de dezenas de painéis, o número de cliques aumenta. E se você pudesse esboçar um painel de controle, tirar uma captura de tela e deixar um profissional de Direito concluir todo o processo para você?</p><p>Neste artigo, vamos fazer isso acontecer. Criaremos um aplicativo que captura uma imagem de um painel, analisa nossos mapeamentos e, em seguida, gera um painel sem que precisemos usar o Kibana!</p><p><strong>Passos</strong>:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#background-&amp;-application-workflow">Contexto e fluxo de trabalho do aplicativo</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#prepare-data">Preparar dados</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#llm-configuration">Configuração LLM</a></li><li><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#application-functions">Funções do aplicativo</a></li></ol><h2>Contexto e fluxo de trabalho do aplicativo</h2><p>A primeira ideia que me veio à mente foi deixar o LLM gerar todo o formato NDJSON <a href="https://www.elastic.co/docs/explore-analyze/find-and-organize/saved-objects">dos objetos salvos</a> pelo Kibana e, em seguida, importá-los para o Kibana.</p><p>Experimentamos alguns modelos:</p><ul><li>Gemini 2.5 pro</li><li>GPT o3 / o4-mini-high / 4.1</li><li>Soneto 4 de Claude</li><li>Grok 3</li><li>Deepseek (Deepthink R1)</li></ul><p>E para as sugestões, começamos com algo tão simples quanto:</p><pre><code>You are an Elasticsearch Saved-Object generator (Kibana 9.0).
INPUTS
=====
1. PNG screenshot of a 4-panel dashboard (attached).
2. Index mapping (below) – trimmed down to only the fields present in the screenshot.
3. Example NDJSON of *one* metric visualization (below) for reference.

TASK
====
Return **only** a valid NDJSON array that recreates the dashboard exactly:
* 2 metric panels (Visits, Unique Visitors)
* 1 pie chart (Most used OS)
* 1 vertical bar chart (State Geo Dest)
* Use index pattern `kibana_sample_data_logs`.
* Preserve roughly the same layout (2×2 grid).
* Use `panelIndex` values 1-4 and random `id` strings.
* Kibana version: 9.0</code></pre><p>Apesar de termos analisado <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic#:~:text=Few%2Dshot%20prompting%20involves%20providing%20examples%20of%20the%20types%20of%20queries%20you%20want%20it%20to%20return%2C%20which%20helps%20in%20increasing%20consistency.">poucos exemplos</a> e explicações detalhadas sobre como construir cada visualização, não tivemos sucesso. Se você estiver interessado nessa experiência, pode encontrar detalhes <a href="https://gist.github.com/TomasMurua/a78dc283e115624731beffc98984b70b">aqui</a>.</p><p>O resultado com essa abordagem foi a visualização dessas mensagens ao tentar carregar os arquivos produzidos pelo LLM no Kibana:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2b599443b5613c9f0fc3235581614add5b4b3900-891x98.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d87ccfc033bc00ee8188c5cae18043fbca22784c-741x233.png" style="width: 100%;" /><p>Isso significa que o JSON gerado é inválido ou está mal formatado. Os problemas mais comuns foram o LLM produzir NDJSON incompleto, apresentar parâmetros incorretos ou retornar JSON comum em vez de NDJSON, independentemente de quanto nos esforçássemos para forçar o contrário.</p><p>Inspirados por <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">este artigo</a> – onde <a href="https://www.elastic.co/docs/solutions/search/search-templates">os modelos de pesquisa</a> funcionaram melhor do que o método freestyle do LLM – decidimos fornecer modelos ao LLM em vez de solicitar a geração do arquivo NDJSON completo e, em seguida, usar os parâmetros fornecidos pelo LLM no código para criar as visualizações adequadas. Essa abordagem não decepcionou, além de ser previsível e extensível, já que agora o código realiza o trabalho pesado, e não o LLM.</p><p>O fluxo de trabalho da aplicação será o seguinte:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/52c587cf0cf3517fdd4ee7ab95581dd4f2bce030-725x668.png" style="width: 100%;" /><p></p><p><em>Para simplificar, omitiremos parte do código, mas você pode encontrar o código funcional da aplicação completa neste <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/from-image-idea-to-kibana-dashboard-using-ai.ipynb"><strong>notebook</strong></a>.</em></p><h2>Pré-requisitos</h2><p>Antes de começar o desenvolvimento, você precisará do seguinte:</p><ol><li>Python 3.8 ou superior</li><li>Um ambiente Python <a href="https://docs.python.org/3/library/venv.html">Venv</a></li><li>Uma instância do Elasticsearch em execução, juntamente com seu endpoint e chave de API.</li><li>Uma chave de API da OpenAI armazenada na variável de ambiente com o nome OPENAI_API_KEY:</li></ol><pre><code>export OPENAI_API_KEY="your-openai-api-key"</code></pre><h2>Preparar dados</h2><p>Para os dados, vamos manter a simplicidade e usar os logs de amostra da Elastic. Você pode aprender como importar esses dados para o seu cluster <a href="https://www.elastic.co/docs/manage-data/ingest/sample-data#add-sample-data-sets">aqui</a>.</p><p>Cada documento inclui detalhes sobre o host que enviou as solicitações ao aplicativo, juntamente com informações sobre a própria solicitação e seu status de resposta. Segue abaixo um exemplo de documento:</p><pre><code>{
    "agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24",
    "bytes": 8509,
    "clientip": "70.133.115.149",
    "extension": "css",
    "geo": {
        "srcdest": "US:IT",
        "src": "US",
        "dest": "IT",
        "coordinates": {
            "lat": 38.05134111,
            "lon": -103.5106908
        }
    },
    "host": "cdn.elastic-elastic-elastic.org",
    "index": "kibana_sample_data_logs",
    "ip": "70.133.115.149",
    "machine": {
        "ram": 5368709120,
        "os": "osx"
    },
    "memory": null,
    "message": "70.133.115.149 - - [2018-08-30T23:35:31.492Z] \"GET /styles/semantic-ui.css HTTP/1.1\" 200 8509 \"-\" \"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.50 Safari/534.24\"",
    "phpmemory": null,
    "referer": "http://twitter.com/error/john-phillips",
    "request": "/styles/semantic-ui.css",
    "response": 200,
    "tags": [
        "success",
        "info"
    ],
    "@timestamp": "2025-07-03T23:35:31.492Z",
    "url": "https://cdn.elastic-elastic-elastic.org/styles/semantic-ui.css",
    "utc_time": "2025-07-03T23:35:31.492Z",
    "event": {
        "dataset": "sample_web_logs"
    },
    "bytes_gauge": 8509,
    "bytes_counter": 51201128
}</code></pre><p>Agora, vamos obter os mapeamentos do índice que acabamos de carregar, <code>kibana_sample_data_logs</code>:</p><pre><code>INDEX_NAME = "kibana_sample_data_logs"

es_client = Elasticsearch(
    [os.getenv("ELASTICSEARCH_URL")],
    api_key=os.getenv("ELASTICSEARCH_API_KEY"),
)

result = es_client.indices.get_mapping(index=INDEX_NAME)
index_mappings = result[list(result.keys())[0]]["mappings"]["properties"]</code></pre><p>Vamos passar os mapeamentos junto com a imagem que carregaremos posteriormente.</p><h2>Configuração LLM</h2><p>Vamos configurar o LLM para usar <a href="https://python.langchain.com/docs/concepts/structured_outputs/">saída estruturada</a> para receber uma imagem como entrada e obter um JSON com as informações necessárias para passar à nossa função e gerar os objetos JSON.</p><p>Instalamos as dependências:</p><pre><code>pip install elasticsearch pydantic langchain langchain-openai -q</code></pre><p>O Elasticsearch nos ajudará a recuperar os <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mapeamentos de índice</a>. Pydantic permite definir esquemas em Python para depois solicitar que o LLM os siga, e <a href="https://www.elastic.co/search-labs/integrations/langchain">LangChain</a> é a estrutura que facilita a chamada de LLMs e ferramentas de IA.</p><p>Criaremos um esquema Pydantic para definir a saída desejada do LLM. O que precisamos saber da imagem é o tipo de gráfico, campo, título da visualização e título do painel:</p><pre><code>class Visualization(BaseModel):
    title: str = Field(description="The dashboard title")
    type: List[Literal["pie", "bar", "metric"]]
    field: str = Field(
        description="The field that this visualization use based on the provided mappings"
    )


class Dashboard(BaseModel):
    title: str = Field(description="The dashboard title")
    visualizations: List[Visualization]</code></pre><p>Para a entrada de imagem, enviaremos um painel que acabei de desenhar:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/36441d7b5dc1f3ff2ac2a30710208d57ad41c716-1600x898.jpg" style="width: 100%;" /><p>Agora declaramos a chamada do modelo LLM e o carregamento da imagem. Essa função receberá os mapeamentos do índice do Elasticsearch e uma imagem do painel que desejamos gerar.</p><p>Com <code>with_structured_output</code> podemos usar nosso esquema Pydantic <code>Dashboard</code> como o objeto de resposta que o LLM produzirá. Com <a href="https://docs.pydantic.dev/latest/">o Pydantic</a>, podemos definir modelos de dados com validação, o que garante que a saída do modelo linear linear (LLM) corresponda à estrutura esperada.</p><p>Para converter a imagem para base64 e enviá-la como entrada, você pode usar um <a href="https://www.base64-image.de/">conversor online</a> ou fazer isso <a href="https://www.geeksforgeeks.org/python-convert-image-to-string-and-vice-versa/">por meio de código</a>.</p><pre><code>prompt = f"""
    You are an expert in analyzing Kibana dashboards from images for the version 9.0.0 of Kibana.

    You will be given a dashboard image and an Elasticsearch index mapping.

    Below are the index mappings for the index that the dashboard is based on.
    Use this to help you understand the data and the fields that are available.

    Index Mappings:
    {index_mappings}

    Only include the fields that are relevant for each visualization, based on what is visible in the image.
    """

message = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": prompt},
            {
                "type": "image",
                "source_type": "base64",
                "data": image_base64,
                "mime_type": "image/png",
            },
        ],
    }
]


try:
    llm = init_chat_model("gpt-4.1-mini")
    llm = llm.with_structured_output(Dashboard)
    dashboard_values = llm.invoke(message)

    print("Dashboard values generated by the LLM successfully")
    print(dashboard_values)
except Exception as e:
    print(f"Failed to analyze image and match fields: {str(e)}")</code></pre><p>O LLM já possui contexto sobre os dashboards do Kibana, então não precisamos explicar tudo no prompt, apenas alguns detalhes para garantir que ele não se esqueça de que está trabalhando com o Elasticsearch e o Kibana.</p><p>Vamos analisar a pergunta:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Seção</td><td style="border: 1px solid #ccc; padding: 4px;">Razão</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Você é especialista em analisar dashboards do Kibana a partir de imagens para a versão 9.0.0 do Kibana.</td><td style="border: 1px solid #ccc; padding: 4px;">Ao reforçar isso no Elasticsearch e na versão do Elasticsearch, reduzimos a probabilidade de o LLM gerar parâmetros antigos/inválidos.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Você receberá uma imagem do painel de controle e um mapeamento do índice do Elasticsearch.</td><td style="border: 1px solid #ccc; padding: 4px;">Explicamos que a imagem se refere a painéis de controle para evitar quaisquer interpretações errôneas por parte do LLM.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Abaixo estão os mapeamentos de índice para o índice no qual o painel se baseia. Use-os para ajudá-lo a entender os dados e os campos disponíveis. Mapeamentos de índice: {index_mappings}</td><td style="border: 1px solid #ccc; padding: 4px;">É crucial fornecer os mapeamentos para que o LLM possa selecionar campos válidos dinamicamente. Caso contrário, poderíamos codificar os mapeamentos diretamente aqui, o que é muito rígido, ou confiar na imagem que contém os nomes de campo corretos, o que não é confiável.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Inclua apenas os campos relevantes para cada visualização, com base no que está visível na imagem.</td><td style="border: 1px solid #ccc; padding: 4px;">Precisávamos adicionar esse reforço porque, às vezes, o programa tenta adicionar campos que não são relevantes para a imagem.</td>
                    </tr>
              </tbody>
            </table>
          <p>Isso retornará um objeto com uma matriz de visualizações para exibir:</p><pre><code>"Dashboard values generated by the LLM successfully
title=""Client, Extension, OS, and Response Keyword Analysis""visualizations="[
   "Visualization(title=""Count of Client IP",
   "type="[
      "metric"
   ],
   "field=""clientip"")",
   "Visualization(title=""Extension Keyword Distribution",
   "type="[
      "pie"
   ],
   "field=""extension.keyword"")",
   "Visualization(title=""Most Used OS",
   "type="[
      "bar"
   ],
   "field=""machine.os.keyword"")",
   "Visualization(title=""Response Keyword Distribution",
   "type="[
      "bar"
   ],
   "field=""response.keyword"")"
]</code></pre><h2>Processando a resposta do LLM</h2><p>Nós<strong> </strong>Criamos um painel de exemplo 2x2 e o exportamos em JSON usando a <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-get-dashboards-dashboard">API &quot;Obter um painel&quot;</a>. Em seguida, armazenamos os painéis como modelos de visualização (pizza, barra, métrica), onde podemos substituir alguns parâmetros para criar novas visualizações com campos diferentes, dependendo da pergunta.</p><p>Você pode ver os arquivos JSON do modelo <a href="https://github.com/Delacrobix/elasticsearch-labs/tree/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/supporting-blog-content/from-image-idea-to-kibana-dashboard-using-ai/templates"><strong>aqui</strong></a>. Observe como alteramos os valores dos objetos que queremos substituir posteriormente por {<code>variable_name</code>}<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ec7e1ac0cd8b470df13e60940162b56778acb386-315x234.png" style="width: 100%;" /><p>Com as informações fornecidas pelo LLM, podemos decidir qual modelo usar e quais valores substituir.</p><p><code>fill_template_with_analysis</code> receberão os parâmetros para um único painel, incluindo o modelo JSON da visualização, um título, um campo e as coordenadas da visualização na grade.</p><p>Em seguida, substituirá os valores do modelo e retornará a visualização JSON final.</p><pre><code>def fill_template_with_analysis(
    template: Dict[str, Any],
    visualization: Visualization,
    grid_data: Dict[str, Any],
):
    template_str = json.dumps(template)
    replacements = {
	 "{visualization_id}": str(uuid.uuid4()),
        "{title}": visualization.title,
        "{x}": grid_data["x"],
        "{y}": grid_data["y"],
    }

    if visualization.field:
        replacements["{field}"] = visualization.field

    for placeholder, value in replacements.items():
        template_str = template_str.replace(placeholder, str(value))

    return json.loads(template_str)</code></pre><p>Para simplificar, teremos coordenadas estáticas que atribuiremos aos painéis que o LLM decidir criar e produziremos um painel de controle em grade 2x2, como na imagem acima.</p><pre><code># Filling templates fields
panels = []    
grid_data = [
    {"x": 0, "y": 0},
    {"x": 12, "y": 0},
    {"x": 0, "y": 12},
    {"x": 12, "y": 12},
]


i = 0

for vis in dashboard_values.visualizations:
    for vis_type in vis.type:
        template = templates.get(vis_type, templates.get("bar", {}))
        filled_panel = fill_template_with_analysis(template, vis, grid_data[i])
        panels.append(filled_panel)
        i += 1</code></pre><p>Dependendo do tipo de visualização decidido pelo LLM, escolheremos um modelo de arquivo JSON e substituiremos as informações relevantes usando <code>fill_template_with_analysis</code> , depois adicionaremos o novo painel a uma matriz que usaremos posteriormente para criar o painel de controle.</p><p>Quando o painel estiver pronto, usaremos a <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-dashboards-dashboard-id">API Criar um painel</a> para enviar o novo arquivo JSON ao Kibana e gerar o painel:<br/></p><pre><code>try:
    dashboard_id = str(uuid.uuid4())

    # post request to create the dashboard endpoint
    url = f"{os.getenv('KIBANA_URL')}/api/dashboards/dashboard/{dashboard_id}"

    dashboard_config = {
        "attributes": {
            "title": dashboard_values.title,
            "description": "Generated by AI",
            "timeRestore": True,
            "panels": panels,  # Visualizations with the values generated by the LLM
            "timeFrom": "now-7d/d",
            "timeTo": "now",
        },
    }

    headers = {
        "Content-Type": "application/json",
        "kbn-xsrf": "true",
        "Authorization": f"ApiKey {os.getenv('ELASTICSEARCH_API_KEY')}",
    }

    requests.post(
        url,
        headers=headers,
        json=dashboard_config,
    )

    # Url to the generated dashboard
    dashboard_url = f"{os.getenv('KIBANA_URL')}/app/dashboards#/view/{dashboard_id}"

    print("Dashboard URL: ", dashboard_url)
    print("Dashboard ID: ", dashboard_id)

except Exception as e:
    print(f"Failed to create dashboard: {str(e)}")</code></pre><p>Para executar o script e gerar o painel de controle, execute o seguinte comando no console:</p><pre><code>python <file_name>.py</code></pre><p>O resultado final será semelhante a este:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e909afbf0e47d9a6e0f7bd07dfb2efcfa5cf06ac-921x715.png" style="width: 100%;" /><h2>Conclusão</h2><p>Os profissionais com formação em Letras demonstram suas fortes habilidades visuais ao realizar tarefas de conversão de texto em código ou ao transformar imagens em código. A API de dashboards também permite transformar arquivos JSON em dashboards e, com um LLM e algum código, podemos transformar imagens em um dashboard do Kibana.</p><p>O próximo passo é melhorar a flexibilidade dos elementos visuais do painel de controle, utilizando diferentes configurações de grade, tamanhos e posições do painel. Além disso, oferecer suporte a visualizações e tipos de visualização mais complexos seria uma adição útil a este aplicativo.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Revelando padrões únicos: um guia para agregação de termos relevantes no Elasticsearch.]]></title>
            <description><![CDATA[Aprenda a usar o importante termo "agregação" para descobrir insights em seus dados.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/significant-terms-aggregation-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/significant-terms-aggregation-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Alexander Dávila]]></dc:creator>
            <pubDate>Mon, 07 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/13adbc901837835bb56abf15e377127b017cfac8-1536x1024.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>No Elasticsearch, a <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation">agregação de termos significativos</a> vai além dos <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-terms-aggregation">termos mais comuns</a> para encontrar valores estatisticamente incomuns em um conjunto de dados. Isso nos permite descobrir informações valiosas e padrões não óbvios. Uma agregação de termos significativos fornece uma resposta com dois parâmetros úteis:</p><ul><li><strong>bg_count (contagem de fundo): </strong>Número de documentos encontrados no conjunto de dados original</li><li><strong>doc_count:</strong> Número de documentos encontrados no conjunto de dados resultante.</li></ul><p>Por exemplo, em um conjunto de dados de vendas de telefones celulares, podemos procurar termos significativos nas vendas do iPhone 16, como este:</p><pre><code>GET phone_sales_analysis/_search
{
 "size": 0,
 "query": {
   "term": {
     "phone_model": {
       "value": "iPhone 16"
     }
   }
 },
 "aggs": {
   "significant_cities": {
     "significant_terms": {
       "field": "city_region",
       "size": 1
     }
   }
 }
}</code></pre><p>Então, a resposta nos dá:</p><pre><code>{
 "aggregations": {
   "significant_cities": {
     "doc_count": 122,
     "bg_count": 424,
     "buckets": [
       {
         "key": "Houston",
         "doc_count": 12,
         "score": 0.1946481360617346,
         "bg_count": 14
       }

     ]
   }
 }
}</code></pre><p>Houston não está entre as 10 principais cidades em todo o conjunto de dados, nem é a principal cidade para o iPhone 16. No entanto, a análise de agregação de termos relevantes mostrou que o<strong><em> iPhone 16 está sendo comprado de forma desproporcional nesta cidade</em></strong> em comparação com o restante dos dados. Vamos analisar os números mais detalhadamente:</p><ul><li><strong>No nível mais alto:</strong><ul><li><strong>doc_count: 122 — </strong>A consulta encontrou um total de 122 documentos.</li><li><strong>bg_count: 424 — </strong>O conjunto de fundo (todos os documentos de vendas) contém 424 documentos.</li></ul></li><li><strong>Na cesta de Houston:</strong><ul><li><strong>doc_count: 12 — </strong>Houston aparece em 12 dos 122 resultados da consulta</li><li><strong>contagem_de_fundo: 14 — </strong>Houston aparece em 14 dos 424 documentos totais no conjunto de dados de fundo.</li></ul></li></ul><p>Isso nos indica que, de um total de 424 compras, apenas 14 ocorreram em Houston; ou seja, 3,3% do total de compras. No entanto, se analisarmos apenas as vendas do iPhone 16, veremos que 12 das 122 unidades foram vendidas em Houston, o que representa 9,8%, três vezes mais do que em todo o conjunto de dados; isso é significativo!</p><p>Veja como isso se apresenta em uma visualização: Total de vendas por cidade/região.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f31335b0b7793650025f941820f238dd35bfb09f-1486x1066.png" style="width: 100%;" /><p>Podemos ver que houve 14 vendas em Houston, o que a torna a 14ª cidade com maior número de vendas no conjunto de dados.</p><p>Agora, se aplicarmos um filtro para analisar apenas as vendas do iPhone 16, temos 12 vendas em Houston, tornando-a a segunda cidade com o maior número de vendas desse modelo específico:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6516040db77e6c62af5541a74c723b18008ad3c6-1472x1038.png" style="width: 100%;" /><h2>Compreendendo os termos significativos de agregação</h2><p>De acordo com a documentação da Elastic, os <a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation">termos significativos de agregação</a> são:</p><p><em>&quot;(Encontra) termos que sofreram uma mudança significativa na popularidade, medida entre um conjunto de primeiro plano e um conjunto de segundo plano.&quot;</em></p><p>Isso significa que utiliza métricas estatísticas para comparar a frequência de um termo em um subconjunto de dados (o conjunto de primeiro plano) com a frequência do mesmo termo no conjunto de dados principal (o conjunto de fundo). Dessa forma, a pontuação reflete a significância estatística em vez da frequência com que um termo aparece nos dados.</p><p>As principais diferenças entre uma agregação de termos significativos e uma agregação de termos normais são:</p><ul><li>A comparação de termos significativos analisa um subconjunto dos dados, enquanto a agregação de termos funciona apenas com o conjunto de dados resultante da consulta.</li><li>Os resultados da agregação de termos mostram os termos mais comuns no conjunto de dados, enquanto os resultados da análise de termos significativos ignoram os termos comuns para encontrar o que torna o conjunto de dados único.</li><li>Termos significativos podem ter um impacto maior no desempenho, visto que precisam obter dados do disco em vez da memória, como faz a agregação de termos.</li></ul><h2>Aplicação prática (análise do comportamento do consumidor)</h2><h3>Preparando os dados para a análise.</h3><p>Para esta análise, geramos um conjunto de dados sintéticos de vendas de telefones celulares, incluindo preço, especificações do aparelho, dados demográficos do comprador e avaliações. Também geramos embeddings a partir do feedback do usuário para podermos executar uma consulta semântica posteriormente. Utilizamos o <a href="https://huggingface.co/intfloat/multilingual-e5-small">modelo multilíngue e5 small</a>, disponível imediatamente no Elasticsearch.</p><p></p><p>Para usar este conjunto de dados no Elasticsearch:</p><ol><li>Faça o upload do arquivo CSV (disponível para download <a href="https://github.com/Alex1795/significant_terms_blog_dataset/blob/main/phone_sales_analysis_dataset.csv">aqui</a>) usando o recurso <a href="https://www.elastic.co/docs/manage-data/ingest/upload-data-files">&quot;Carregar arquivos de dados&quot;</a> do Kibana.</li><li>Configure um campo semântico, como mostrado <a href="https://www.elastic.co/search-labs/blog/chat-with-pdf-elastic-playground#upload-pdfs-to-kibana">neste blog,</a> chamado &quot;embedding&quot;, que utiliza o <code>multilingual-e5-small model</code></li><li>Finalize a importação com os valores padrão do tipo de campo (palavra-chave para todos os campos, exceto <code>purchase_date</code> e <code>user_feedback)</code>. Certifique-se de adicionar o nome do índice <code>phone_sales_analysis</code> para poder executar as consultas apresentadas aqui como estão.</li></ol><p>O principal objetivo desta análise é descobrir <strong><em>&quot;O que diferencia os compradores do iPhone 16 de outros segmentos da população?</em></strong> &quot; e obter uma segmentação desses compradores para fins de marketing. </p><p>Este é um documento de exemplo do conjunto de dados:</p><pre><code>{
         "customer_type": "Returning",
         "user_feedback": "I have to say, quality is great for the price. The battery life is really good.",
         "upgrade_frequency": "2 years",
         "storage_capacity": "256GB",
         "occupation": "Technology & Data",
         "color": "Phantom Black",
         "gender": "Male",
         "price_paid": 899,
         "previous_brand_loyalty": "Mixed",
         "location_type": "Urban",
         "phone_model": "Samsung Galaxy S24",
         "city_region": "San Francisco Bay Area",
         "@timestamp": "2024-03-15T00:00:00.000-05:00",
         "income_bracket": "75000-100000",
         "purchase_channel": "Online",
         "feedback_sentiment": "positive",
         "education_level": "Bachelor",
         "embedding": "I have to say, quality is great for the price. The battery life is really good.",
         "customer_id": "C001",
         "purchase_date": "2024-03-15",
         "age": 34,
         "trade_in_model": "iPhone 13"
}</code></pre><h3>Compreendendo os padrões demográficos</h3><p>Aqui, vamos realizar uma análise na população em geral e compará-la com descobertas interessantes das agregações de termos significativos para usuários do iPhone 16.</p><h4>Padrões normais</h4><p>Para entender os padrões normais de compra, podemos agregar dados de todos os documentos em diferentes áreas. Para simplificar, vamos nos concentrar em explorar as ocupações das pessoas que compraram um telefone. Podemos fazer isso com uma solicitação ao Elasticsearch.</p><pre><code>GET phone_sales_analysis/_search
{
 "aggs": {
   "occupation_distribution": {
     "terms": {
       "size": 5,
       "field": "occupation"
     }
   }
 },
 "size": 0
}</code></pre><p>Isso nos indica que as principais ocupações no conjunto de dados (por número de registros) são:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99136ddddd7abad5d74481158a04501b6915441b-1518x480.png" style="width: 100%;" /><h4>Padrões de usuários do iPhone 16</h4><p>Para entender o que diferencia as pessoas que compraram um iPhone 16, vamos executar uma agregação de termos no mesmo campo com um filtro para encontrar essas pessoas na consulta, assim:</p><pre><code>GET phone_sales_analysis/_search
{
  "query": {
    "term": {
      "phone_model": "iPhone 16"
    }
  },
  "aggs": {
    "occupation_distribution": {
      "terms": {
        "size": 5,
        "field": "occupation"
      }
    }
  },
  "size": 0
}</code></pre><p>Assim, para os usuários do iPhone 16, as principais funções são:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/36ce86475beb03810c6ad81d7c776d1eec736654-1500x484.png" style="width: 100%;" /><p>Podemos observar que os usuários do iPhone 16 apresentam padrões de ocupação diferentes em comparação com os usuários de outros modelos de telefone. Vamos usar o Kibana para visualizar os resultados com facilidade:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b70ab05bc962a274e1617b6caf20575c489a62d8-1448x1128.png" style="width: 100%;" /><p></p><p>Neste gráfico, podemos ver que a tendência do iPhone 16 é diferente da tendência da população em geral.</p><p>Podemos pular toda essa análise e ver o que diferencia os usuários do iPhone 16 da população em geral, executando uma agregação de termos significativos:</p><pre><code>GET phone_sales_analysis/_search
{
  "query": {
    "term": {
      "phone_model": "iPhone 16"
    }
  },
  "aggs": {
    "occupation_distribution": {
      "significant_terms": {
        "size": 5,
        "field": "occupation"
      }
    }
  },
  "size": 0
}</code></pre><p>Resumindo, obtemos esta resposta:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Valores das ocupações para o iPhone 16</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de documentos</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de fundo</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">distribuição_de_ocupações (nível superior)</td><td style="border: 1px solid #ccc; padding: 4px;">122</td><td style="border: 1px solid #ccc; padding: 4px;">424</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Categoria Médica e de Saúde</td><td style="border: 1px solid #ccc; padding: 4px;">45</td><td style="border: 1px solid #ccc; padding: 4px;">57</td>
                    </tr>
              </tbody>
            </table>
          <p>A resposta sugere claramente que os usuários do iPhone 16 têm uma característica incomum (leia-se significativa!). Número de pessoas na área médica e de saúde em comparação com a população em geral. Vamos analisar o que os números na resposta significam:</p><ul><li><strong>No nível mais alto:</strong><ul><li><strong>doc_count: 122 — </strong>A consulta encontrou um total de 122 documentos.</li><li><strong>bg_count: 424 — </strong>O conjunto de fundo (todos os documentos de vendas) contém 424 documentos.</li></ul></li><li><strong>Na categoria Medicina e Saúde:</strong><ul><li><strong>doc_count: 45 — </strong>&quot;Medicina e Saúde&quot; aparece em 45 dos 122 resultados da consulta</li><li><strong>bg_count: 57 — </strong>&quot;Medical &amp; Healthcare&quot; aparece em 57 dos 424 documentos totais no conjunto de dados de fundo.</li></ul></li></ul><p>Dos 424 compradores, 57 trabalham na área médica e de saúde — ou seja, 13,44%. Mas quando analisamos os compradores do iPhone 16, 45 de 122 trabalham na área médica e de saúde — ou seja, 36,88%. Isso significa que temos o dobro de probabilidade de encontrar alguém que trabalhe na área médica e de saúde entre os usuários do iPhone 16!</p><p>Podemos aplicar essa mesma análise a outros campos (idade, localização, faixa de renda, etc.) para encontrar mais informações sobre o que torna os usuários do iPhone 16 únicos. </p><h3>Segmentação de consumidores</h3><p>Podemos usar o conceito de agregação para extrair informações sobre as relações entre produtos, categorias e segmentos de clientes. Para isso, construímos uma agregação principal para a categoria que temos interesse em explorar. Também utilizamos uma subagregação de termos significativos e uma subagregação de termos normais para encontrar informações interessantes sobre essa categoria e compará-las com o que a maioria das pessoas nessa profissão utiliza.</p><p>Por exemplo, vejamos o que as pessoas em algumas áreas de trabalho preferem:</p><ol><li>Para tornar a análise mais clara, vamos limitar nossa busca a 3 áreas de atuação: [&quot;Administrativo e Suporte&quot;, &quot;Tecnologia e Dados&quot;, &quot;Médico e Saúde&quot;]</li><li>No que diz respeito às agregações, começamos com uma agregação de termos por ocupação.</li><li>Adicione uma subagregação: termos por modelo de telefone — para descobrir quais modelos os usuários que trabalham em cada área estão comprando.</li><li>Adicione uma segunda subagregação: termos significativos por modelos telefônicos — para descobrir quais modelos são específicos em cada área de atuação.</li></ol><pre><code>GET phone_sales_analysis/_search
{
 "query": {
   "terms": {
     "occupation": [
       "Administrative & Support",
       "Technology & Data",
       "Medical & Healthcare"
     ]
   }
 },
 "aggs": {
   "occupations": {
     "terms": {
       "size": 15,
       "field": "occupation"
     },
     "aggs": {
       "general_models": {
         "terms": {
           "field": "phone_model"
         }
       },
       "significant_models": {
         "significant_terms": {
           "field": "phone_model"
         }
       }
     }
   }
 },
 "size": 0
}</code></pre><p>Vamos analisar os resultados da agregação:</p><p><strong>Ocupação</strong>: Administrativo e de Apoio</p><p><strong>Agregação de termos</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a4ad519c9013867a3f4cee032160eadd8a47804a-1506x398.png" style="width: 100%;" /><p><strong>Agregação de termos significativos</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e5604fa8036667bdfe733576a5e7c6153760dd3a-306x220.png" style="width: 100%;" /><p>A partir desta tabela, podemos inferir que não existem diferenças significativas entre a tendência para esta ocupação e a tendência para toda a população.</p><p><strong>Área de atuação</strong>: Tecnologia e Dados</p><p><strong>Agregação de termos</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/13b09039bb7d183276451007d2d69dc190b1d3c0-1508x836.png" style="width: 100%;" /><p></p><p><strong>Agregação de termos significativos</strong></p><p>Total de documentos: 424</p><p>Documentos nesta ocupação: 71</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">modelo de telefone</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de documentos (este modelo nesta ocupação)</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de fundo (este modelo em todos os documentos)</td><td style="border: 1px solid #ccc; padding: 4px;">% em todos os documentos</td><td style="border: 1px solid #ccc; padding: 4px;">% nesta ocupação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Google Pixel 8</td><td style="border: 1px solid #ccc; padding: 4px;">12</td><td style="border: 1px solid #ccc; padding: 4px;">22</td><td style="border: 1px solid #ccc; padding: 4px;">5,19%</td><td style="border: 1px solid #ccc; padding: 4px;">16,90%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 11</td><td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">14</td><td style="border: 1px solid #ccc; padding: 4px;">3,30%</td><td style="border: 1px solid #ccc; padding: 4px;">12,68%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 12 Pro</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">0,71%</td><td style="border: 1px solid #ccc; padding: 4px;">4,23%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Google Pixel 8 Pro</td><td style="border: 1px solid #ccc; padding: 4px;">9</td><td style="border: 1px solid #ccc; padding: 4px;">21</td><td style="border: 1px solid #ccc; padding: 4px;">4,95%</td><td style="border: 1px solid #ccc; padding: 4px;">12,68%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Nada Telefone 2</td><td style="border: 1px solid #ccc; padding: 4px;">5</td><td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">1,89%</td><td style="border: 1px solid #ccc; padding: 4px;">7,04%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Samsung Galaxy Z Fold5</td><td style="border: 1px solid #ccc; padding: 4px;">4</td><td style="border: 1px solid #ccc; padding: 4px;">6</td><td style="border: 1px solid #ccc; padding: 4px;">1,42%</td><td style="border: 1px solid #ccc; padding: 4px;">5,63%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">OnePlus 12</td><td style="border: 1px solid #ccc; padding: 4px;">8</td><td style="border: 1px solid #ccc; padding: 4px;">20</td><td style="border: 1px solid #ccc; padding: 4px;">4,72%</td><td style="border: 1px solid #ccc; padding: 4px;">11,27%</td>
                    </tr>
              </tbody>
            </table>
          <p><strong>Área de atuação</strong>: Medicina e Saúde</p><p><strong>Agregação de termos</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b008e996742fc0bb48dc6bacff17cfbc56cf0d73-1492x398.png" style="width: 100%;" /><p><strong>Agregação de termos significativos</strong></p><p>Total de documentos: 424</p><p>Documentos nesta ocupação: 57</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">modelo de telefone</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de documentos (este modelo nesta ocupação)</td><td style="border: 1px solid #ccc; padding: 4px;">contagem de fundo (este modelo em todos os documentos)</td><td style="border: 1px solid #ccc; padding: 4px;">% em todos os documentos</td><td style="border: 1px solid #ccc; padding: 4px;">% nesta ocupação</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 16</td><td style="border: 1px solid #ccc; padding: 4px;">45</td><td style="border: 1px solid #ccc; padding: 4px;">122</td><td style="border: 1px solid #ccc; padding: 4px;">28,77%</td><td style="border: 1px solid #ccc; padding: 4px;">78,95%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 15 Pro Max</td><td style="border: 1px solid #ccc; padding: 4px;">3</td><td style="border: 1px solid #ccc; padding: 4px;">13</td><td style="border: 1px solid #ccc; padding: 4px;">3,07%</td><td style="border: 1px solid #ccc; padding: 4px;">5,26%</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">iPhone 15</td><td style="border: 1px solid #ccc; padding: 4px;">7</td><td style="border: 1px solid #ccc; padding: 4px;">40</td><td style="border: 1px solid #ccc; padding: 4px;">9,43%</td><td style="border: 1px solid #ccc; padding: 4px;">12,28%</td>
                    </tr>
              </tbody>
            </table>
          <p>Vejamos o que esses dados nos contam:</p><ul><li>Profissionais da área médica e de saúde preferem o iPhone 16 e, de modo geral, são muito inclinados a usar telefones da Apple.</li><li>Profissionais de tecnologia e dados preferem celulares Android de última geração, mas não necessariamente usam a marca Samsung. Existe também uma tendência considerável para iPhones nesta categoria.</li><li>Profissionais administrativos e de suporte preferem celulares Samsung e Google, mas não há uma tendência forte e única entre eles.</li></ul><h3>Agregação de termos relevantes e busca híbrida</h3><p>A busca híbrida combina a busca por texto com resultados semânticos para proporcionar uma experiência de busca aprimorada. Nesse contexto, uma agregação de termos significativa pode fornecer insights sobre os resultados de uma busca contextualizada, respondendo à pergunta: <strong>O que torna este conjunto de dados especial em comparação com todos os outros documentos?</strong>Para demonstrar essa funcionalidade, vejamos quais modelos são mais frequentes quando os usuários mencionam um bom desempenho: </p><ul><li>Vamos construir uma consulta semântica onde encontramos o feedback de usuário mais relevante para a entrada &quot;bom desempenho&quot; no campo de incorporação.</li><li>Também utilizaremos uma pesquisa de texto com os mesmos termos no campo de texto user_feedback.</li><li>Também adicionaremos uma consulta de termos relevantes para encontrar modelos de telefone que sejam mais frequentes nesses resultados do que no conjunto de dados completo.<br/></li></ul><pre><code>GET phone_sales_analysis/_search
{
 "retriever": {
   "rrf": {
     "retrievers": [
       {
         "standard": {
           "query": {
             "bool": {
               "must": [
                 {
                   "match": {
                     "user_feedback": {
                       "query": "good performance",
                       "operator": "and"
                     }
                   }
                 }
               ]
             }
           }
         }
       },
       {
         "standard": {
           "query": {
             "semantic": {
               "field": "embedding",
               "query": "good performance"
             }
           }
         }
       }
     ],
    "rank_window_size": 20
   }
 },
 "aggs": {
   "Models": {
     "significant_terms": {
       "field": "phone_model"
     }
   }
 }
}</code></pre><p>Vejamos um exemplo dos documentos correspondentes:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4cb488097a382f0c28c21540db4f593d23633473-1600x162.png" style="width: 100%;" /><p>Esta é a resposta que recebemos:</p><pre><code>{
  "took": 388,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 20,
      "relation": "eq"
    },
    "max_score": 0.016393442,
    "hits": [...]
  },
  "aggregations": {
    "Models": {
      "doc_count": 20,
      "bg_count": 424,
      "buckets": [
        {
          "key": "iPhone 15",
          "doc_count": 5,
          "score": 0.4125,
          "bg_count": 40
        }
      ]
    }
  }
}</code></pre><p></p><p>Isso nos mostra que, embora o iPhone 15 seja encontrado 40 vezes em um total de 424 documentos (9,4% dos documentos), ele aparece 5 vezes nos 20 documentos que corresponderam à busca semântica &quot;bom desempenho&quot; (25% dos documentos). Assim, podemos concluir que: a probabilidade de um iPhone 15 ser mencionado como tendo um bom desempenho é 2,7 vezes maior do que por acaso.</p><h2>Conclusão</h2><p>A agregação de termos relevantes pode revelar detalhes únicos de um conjunto de dados, comparando-o com o universo de documentos. Isso pode revelar relações inesperadas em nossos dados, indo além da contagem de ocorrências. Podemos aplicar termos significativos em diversos casos de uso que possibilitam funcionalidades muito interessantes, por exemplo:</p><ul><li><a href="https://www.elastic.co/blog/significant-terms-aggregation#credit">Ao trabalhar na detecção de fraudes, procure padrões </a>— identifique transações comuns em cartões de crédito roubados.</li><li>Análises da qualidade da marca a partir de avaliações de usuários — identifique uma marca com um número desproporcional de avaliações negativas.</li><li><a href="https://www.elastic.co/docs/reference/aggregations/search-aggregations-bucket-significantterms-aggregation#_use_on_free_text_fields">Identificar </a>documentos classificados incorretamente — identificar documentos que pertencem a uma categoria (filtro de termos) e que usam palavras incomuns para essa categoria em sua descrição (agregação de termos relevantes).</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Construindo um assistente RAG ativo com JavaScript, Mastra e Elasticsearch.]]></title>
            <description><![CDATA[Aprenda a criar agentes de IA no ecossistema JavaScript.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/agentic-rag</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/agentic-rag</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Essa ideia me ocorreu durante uma acirrada e decisiva liga de basquete de fantasia. Eu me perguntei: <em>será que eu conseguiria criar um agente de IA que me ajudasse a dominar meus confrontos semanais? Com certeza!</em></p><p>Neste artigo, exploraremos como construir um assistente RAG agente usando <a href="https://mastra.ai/en/docs">o Mastra</a> e um aplicativo web JavaScript leve para interagir com ele. Ao conectar este agente ao Elasticsearch, damos a ele acesso a dados estruturados dos jogadores e a capacidade de executar agregações estatísticas em tempo real, para fornecer recomendações baseadas em estatísticas dos jogadores. Acesse o <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repositório</a> do GitHub para acompanhar; o <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/README.md">arquivo README</a> fornece instruções sobre como clonar e executar o aplicativo por conta própria. </p><p>Eis como deverá ficar quando tudo estiver montado:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" style="width: 100%;" /><p>Nota: Este post do blog complementa o artigo “<a href="https://www.elastic.co/search-labs/blog/ai-agents-ai-sdk-elasticsearch">Criando agentes de IA com o SDK de IA e o Elastic</a>”. Se você é iniciante no estudo de agentes de IA em geral e em suas possíveis aplicações, comece por aí.<br/></p><h2><strong>Visão geral da arquitetura</strong></h2><p>No núcleo do sistema está um modelo de linguagem abrangente (LLM, na sigla em inglês), que atua como o motor de raciocínio do agente (o cérebro). Ele interpreta a entrada do usuário, decide quais ferramentas utilizar e orquestra as etapas necessárias para gerar uma resposta relevante.</p><p>O próprio agente é estruturado pelo Mastra, um framework de agentes no ecossistema JavaScript. O Mastra integra o LLM com infraestrutura de backend, expõe-no como um endpoint de API e fornece uma interface para definir ferramentas, prompts do sistema e comportamento do agente.</p><p>Na interface, usamos <a href="https://vite.dev/guide/">o Vite</a> para criar rapidamente um aplicativo web React que fornece uma interface de chat para enviar perguntas ao agente e receber suas respostas.</p><p>Por fim, temos o Elasticsearch, que armazena estatísticas de jogadores e dados de confrontos que o agente pode consultar e agregar.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" style="width: 100%;" /><h2><strong>Histórico</strong></h2><p>Vamos revisar alguns conceitos fundamentais:</p><h3><strong>O que é RAG agentivo?</strong></h3><p>Os agentes de IA podem interagir com outros sistemas, operar de forma independente e executar ações com base em parâmetros definidos por eles. O Agentic RAG combina a autonomia de um agente de IA com os princípios da geração aumentada por recuperação, permitindo que um LLM escolha quais ferramentas utilizar e quais dados usar como contexto para gerar uma resposta. Leia mais sobre a RAG <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">aqui</a>.</p><h3><strong>Ao escolher uma estrutura, por que ir além do AI-SDK?</strong></h3><p>Existem muitas estruturas de agentes de IA disponíveis e você provavelmente já ouviu falar das mais populares, como <a href="https://www.elastic.co/search-labs/blog/using-crewai-with-elasticsearch">CrewAI</a>, <a href="https://www.elastic.co/search-labs/blog/using-autogen-with-elasticsearch">AutoGen</a> e <a href="https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch">LangGraph</a>. A maioria dessas estruturas compartilha um conjunto comum de funcionalidades, incluindo suporte para diferentes modelos, uso de ferramentas e gerenciamento de memória.</p><p>Segue abaixo uma <a href="https://docs.google.com/spreadsheets/d/1B37VxTBuGLeTSPVWtz7UMsCdtXrqV5hCjWkbHN8tfAo/edit?gid=0#gid=0">tabela comparativa</a> de frameworks elaborada por Harrison Chase (CEO da LangChain).</p><p>O que despertou meu interesse no Mastra foi o fato de ser um framework que prioriza o JavaScript, criado para que desenvolvedores full-stack possam integrar agentes facilmente em seu ecossistema. O SDK de IA da Vercel também faz a maior parte disso, mas o grande diferencial do Mastra é quando seus projetos incluem fluxos de trabalho de agentes mais complexos. O Mastra aprimora os padrões básicos definidos pelo AI-SDK e, neste projeto, usaremos os dois em conjunto.</p><h3><strong>Considerações sobre estruturas e escolha de modelos</strong></h3><p>Embora essas estruturas possam ajudá-lo a criar agentes de IA rapidamente, existem algumas desvantagens a serem consideradas. Por exemplo, ao usar qualquer outra estrutura fora dos agentes de IA ou de qualquer camada de abstração em geral, você perde um pouco do controle. Se o LLM não usar as ferramentas corretamente ou fizer algo que você não deseja, a abstração dificulta a depuração. Ainda assim, na minha opinião, essa troca vale a pena pela facilidade e rapidez que se obtém ao construir, especialmente porque essas estruturas estão ganhando força e sendo constantemente aprimoradas.</p><p>Novamente, essas estruturas são agnósticas em relação ao modelo, o que significa que você pode usar diferentes modelos sem problemas. Lembre-se de que os modelos variam nos conjuntos de dados em que foram treinados e, consequentemente, variam nas respostas que fornecem. Alguns modelos sequer suportam a chamada de ferramentas. Portanto, é possível alternar e testar diferentes modelos para ver qual oferece as melhores respostas, mas lembre-se de que provavelmente você terá que reescrever o prompt do sistema para cada um deles. Por exemplo, usando Llama3.3 Em comparação com o GPT-40, é necessário muito mais estímulo e instruções específicas para obter a resposta desejada.</p><h3><strong>Basquete de fantasia da NBA</strong></h3><p>O basquete de fantasia envolve a criação de uma liga com um grupo de amigos (atenção: dependendo do nível de competitividade do grupo, isso pode afetar o status das suas amizades), geralmente com algum dinheiro em jogo. Cada um de vocês monta uma equipe de 10 jogadores para competir contra a equipe de 10 jogadores de um amigo, alternando semanalmente. Os pontos que contribuem para a sua pontuação geral são definidos pelo desempenho de cada um dos seus jogadores contra os adversários em uma determinada semana.</p><p>Se um jogador da sua equipe se lesionar, for suspenso, etc., existe uma lista de jogadores disponíveis no mercado para adicionar à sua equipe. É aqui que entra em jogo grande parte da estratégia complexa nos esportes de fantasia, porque você tem um número limitado de jogadores para escolher e todos estão constantemente em busca do melhor jogador.</p><p>É aqui que nosso assistente de IA da NBA brilhará, especialmente em situações em que você precisa decidir rapidamente qual jogador escolher. Em vez de ter que pesquisar manualmente o desempenho de um jogador contra um adversário específico, o assistente pode encontrar esses dados rapidamente e comparar as médias para fornecer uma recomendação precisa.</p><p>Agora que você já conhece alguns conceitos básicos sobre RAG agentivo e basquete fantasy da NBA, vamos ver como funciona na prática.</p><h2><strong>Construindo o projeto</strong></h2><p>Se você ficar preso em algum ponto ou não quiser construir tudo do zero, consulte o <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repositório</a>.</p><h3><strong>O que abordaremos</strong></h3><ol><li><strong>Estruturando o projeto:</strong><ol><li><strong>Backend (Mastra):</strong> Use o comando `npx create mastra@latest` para criar a estrutura do backend e definir a lógica do agente.</li><li><strong>Frontend (Vite + React):</strong> Use o comando `npm create vite@latest` para criar a interface de chat do frontend para interação com o agente.</li></ol></li><li><strong>Configurando variáveis de ambiente</strong><ol><li>Instale o dotenv para gerenciar variáveis de ambiente.</li><li>Crie um arquivo .env arquive e forneça as variáveis necessárias.</li></ol></li><li><strong>Configurando o Elasticsearch</strong><ol><li>Crie um cluster Elasticsearch (localmente ou na nuvem).</li><li>Instale o cliente oficial do Elasticsearch.</li><li>Garanta que as variáveis de ambiente estejam acessíveis.</li><li>Estabelecer conexão com o cliente.</li></ol></li><li><strong>Ingestão em massa de dados da NBA no Elasticsearch</strong><ol><li>Crie um índice com os mapeamentos apropriados para habilitar agregações.</li><li>Importar em massa estatísticas de jogo de jogadores de um arquivo CSV para um índice do Elasticsearch.</li></ol></li><li><strong>Definir agregações do Elasticsearch</strong><ol><li>Consulta para calcular as médias históricas contra um adversário específico.</li><li>Consulta para calcular as médias da temporada contra um adversário específico.</li></ol></li><li><strong>Arquivo utilitário de comparação de jogadores</strong><ol><li>Consolida funções auxiliares e agregações do Elasticsearch.</li></ol></li><li><strong>Construindo o agente</strong><ol><li>Adicione a definição do agente e o prompt do sistema.</li><li>Instale o Zod e defina as ferramentas.</li><li>Adicionar configuração de middleware para lidar com CORS.</li></ol></li><li><strong>Integrando o frontend</strong><ol><li>Utilizando o useChat do AI-SDK para interagir com o agente.</li><li>Crie a interface do usuário para manter conversas formatadas adequadamente.</li></ol></li><li><strong>Executando o aplicativo</strong><ol><li>Inicie tanto o backend (servidor Mastra) quanto o frontend (aplicativo React).</li><li>Exemplos de consultas e uso.</li></ol></li><li><strong>O que vem a seguir: tornar o agente mais inteligente.</strong><ol><li>Adicionando recursos de busca semântica para possibilitar recomendações mais relevantes.</li><li>Habilite consultas dinâmicas movendo a lógica de busca para o servidor Elasticsearch MCP (Model Context Protocol).</li></ol></li></ol><h3><strong>Pré-requisitos</strong></h3><ul><li><strong>Node.js e npm</strong>: Tanto o backend quanto o frontend são executados em Node. Certifique-se de ter o Node 18+ e o npm v9+ instalados (que já vêm incluídos no Node 18+).</li><li><strong>Cluster Elasticsearch:</strong> Um cluster Elasticsearch ativo, seja localmente ou na nuvem.</li><li><strong>Chave da API da OpenAI</strong>: Gere uma na página de chaves da API no <a href="https://platform.openai.com/api-keys">portal de desenvolvedores da OpenAI</a>.</li></ul><p></p><h3><strong>Estrutura do projeto</strong></h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" style="width: 100%;" /><h4><strong>Etapa 1: Estruturando o projeto</strong></h4><ol><li>Primeiro, crie o diretório nba-ai-assistant-js e navegue até ele usando: </li></ol><pre><code>mkdir nba-ai-assistant-js && cd nba-ai-assistant-js</code></pre><p><strong>Backend:</strong></p><ol><li>Utilize a ferramenta de criação do Mastra com o comando: </li></ol><pre><code>npx create-mastra@latest</code></pre><p>2. Você deverá receber algumas mensagens no seu terminal. Para a primeira, vamos nomear o backend do projeto:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" style="width: 100%;" /><p>3. Em seguida, manteremos a estrutura padrão para armazenar os arquivos Mastra, então insira <code>src/</code>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" style="width: 100%;" /><p>4. Em seguida, escolheremos a OpenAI como nosso provedor padrão de LLM.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" style="width: 100%;" /><p>5. Por fim, será solicitada a sua chave de API da OpenAI. Por agora, vamos escolher a opção de ignorar e fornecer isso mais tarde em um arquivo<code> .env</code> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" style="width: 100%;" /><p><strong>Front-end:</strong></p><ol><li>Volte ao diretório raiz e execute a <a href="https://vite.dev/guide/">ferramenta de criação do Vite</a> usando este comando: <code>npm create vite@latest frontend -- --template react</code></li></ol><p>Isso deverá criar um aplicativo React leve chamado <code>frontend</code> com um modelo específico para React.</p><p>Se tudo correr bem, dentro do diretório do seu projeto, você deverá ver um diretório backend que contém o código Mastra e um diretório <code>frontend</code> com seu aplicativo React.</p><p></p><h4><strong>Etapa 2: Configurando as variáveis de ambiente</strong></h4><ol><li>Para gerenciar chaves sensíveis, usaremos o pacote <code>dotenv</code> para carregar nossas variáveis de ambiente do arquivo .env. arquivo. Navegue até o diretório backend e instale <code>dotenv</code>:</li></ol><pre><code>cd backend
npm install dotenv --save</code></pre><p>2. No diretório backend, um arquivo example.env é fornecido com as variáveis apropriadas para preenchimento. Se você criar o seu próprio, certifique-se de incluir as seguintes variáveis:</p><pre><code># OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here

# Elasticsearch Configuration
ELASTIC_ENDPOINT=your_elasticsearch_endpoint_here
ELASTIC_API_KEY=your_elasticsearch_api_key_here
</code></pre><p></p><p>Nota: Certifique-se de que este arquivo seja excluído do seu controle de versão adicionando <code>.env</code> a <code>.gitignore</code>.</p><h4><strong>Etapa 3: Configurando o Elasticsearch</strong></h4><p>Primeiro, você precisa de um cluster Elasticsearch ativo. Existem duas opções:</p><ul><li><strong>Opção A: Usar o Elasticsearch Cloud</strong><ul><li>Inscreva-se no <a href="https://cloud.elastic.co/registration">Elastic Cloud.</a></li><li>Criar uma nova implantação</li><li>Obtenha o URL do seu endpoint e a chave da API (codificada).</li></ul></li><li><strong>Opção B: Executar o Elasticsearch localmente</strong><ul><li>Instale e execute o Elasticsearch localmente.</li><li>Use http://localhost:9200 como seu endpoint.</li><li>Gere uma chave de API</li></ul></li></ul><p></p><p><strong>Instalando o cliente Elasticsearch no servidor:</strong></p><ol><li>Primeiro, instale o cliente oficial do Elasticsearch no diretório do seu backend:</li></ol><pre><code>npm install @elastic/elasticsearch</code></pre><p>2. Em seguida, crie um diretório chamado lib para armazenar funções reutilizáveis e navegue até ele:</p><pre><code>mkdir lib && cd lib</code></pre><p>3. Dentro da pasta, crie um novo arquivo chamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticClient.js">elasticClient.js</a>. Este arquivo inicializará o cliente Elasticsearch e o disponibilizará para uso em todo o seu projeto.</p><p>4. Como estamos usando módulos ECMAScript (ESM), os nomes de arquivo __dirname and __não estão disponíveis. Para garantir que suas variáveis de ambiente sejam carregadas corretamente a partir do arquivo .env No arquivo localizado na pasta backend, adicione esta configuração ao início do seu arquivo:</p><pre><code>import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { Client } from '@elastic/elasticsearch';

// Grab current directory and load .env from backend folder
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '../.env');

// Load environment variables from the correct path
config({ path: envPath });</code></pre><p>5. Agora, inicialize o cliente Elasticsearch usando suas variáveis de ambiente e verifique a conexão:</p><pre><code>//Elastic client Initialization, make sure environment variables are being loaded in correctly
const config= {
    node: `${process.env.ELASTIC_ENDPOINT}`,
    auth: {
        apiKey: `${process.env.ELASTIC_API_KEY}`,
    },
};

export const elasticClient = new Client(config);

//Check if the client is connected
async function checkConnection() { 
    try {
        const info = await elasticClient.info();
        console.log('Elasticsearch is connected:', info);
    } catch (error) {
        console.error('Elasticsearch connection error:', error);
    }
}

checkConnection();
</code></pre><p>Agora, podemos importar essa instância de cliente para qualquer arquivo que precise interagir com o seu cluster Elasticsearch.</p><p></p><h4><strong>Etapa 4: Ingestão em massa de dados da NBA no Elasticsearch</strong></h4><p><strong>Conjunto de dados:</strong></p><p>Para este projeto, utilizaremos como referência os conjuntos de dados disponíveis no diretório <a href="https://github.com/jdarmada/nba-ai-assistant-js/tree/main/backend">backend/data</a> do repositório. Nosso assistente da NBA usará esses dados como base de conhecimento para realizar comparações estatísticas e gerar recomendações.</p><ul><li><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - Estatísticas de jogo de jogadores (por exemplo, pontos, rebotes, roubos de bola, etc., por jogo, por jogador, ao longo de toda a sua carreira na NBA). Usaremos esse conjunto de dados para realizar agregações. (Observação: estes são dados fictícios, pré-gerados para fins de demonstração e não provenientes de fontes oficiais da NBA.)</li><li><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/playerAndTeamInfo.js">playerAndTeamInfo.js</a> - Substitui os metadados de jogadores e equipes que normalmente seriam fornecidos por uma chamada de API, permitindo que o agente associe nomes de jogadores e equipes a IDs. Como estamos usando dados de exemplo, não queremos a sobrecarga de buscar dados em uma API externa, então definimos alguns valores fixos que o agente pode referenciar.</li></ul><p></p><p><strong>Implementação:</strong></p><ol><li>No diretório <code>backend/lib</code> , crie um arquivo chamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/playerDataIngestion.js">playerDataIngestion.js</a>.</li><li>Configure as importações, resolva o caminho do arquivo CSV e configure a análise sintática. Novamente, como estamos usando ESM, precisamos reconstruir <code>__dirname</code> para resolver o caminho para o CSV de amostra. Além disso, importaremos <a href="http://node.js/">o Node.js.</a> módulos integrados, <code>fs</code> e <code>readline</code>, para analisar o arquivo CSV fornecido linha por linha.</li></ol><pre><code>import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import { elasticClient } from './elasticClient.js';

const indexName = 'sample-nba-player-data'; //Replace with your preferred index name

//Since we are using ES modules __dirname and __filename don't exist, so this is a workaround that allows us to use the absolute file path for our sample data.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, '../data/sample_nba_data.csv');</code></pre><p>Isso prepara você para ler e analisar o CSV de forma eficiente quando chegarmos à etapa de ingestão em massa.</p><p>3. Crie um índice com o mapeamento apropriado. Embora o Elasticsearch possa inferir automaticamente os tipos de campo com <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dynamic">mapeamento dinâmico</a>, queremos ser explícitos aqui para que cada estatística seja tratada como um campo numérico. Isso é importante porque usaremos esses campos para agregações mais tarde. Também queremos usar o tipo <code>float </code>para estatísticas como pontos, rebotes, etc., para garantir que incluamos valores decimais. Finalmente, queremos adicionar a propriedade de mapeamento <code>dynamic: &#x27;strict&#x27;</code> para que o Elasticsearch não mapeie dinamicamente campos não reconhecidos. <br/></p><pre><code>// Function to create an index with mappings
async function createIndex() {
    try {
        // Check if the index already exists
        const exists = await elasticClient.indices.exists({ index: indexName });

        if (exists) {
            console.log(`Index "${indexName}" already exists, deleting it now.`);
            await elasticClient.indices.delete({ index: indexName });
            console.log(`Deleted index "${indexName}".`);
        }
        // Create the index with mappings
        const response = await elasticClient.indices.create({
            index: indexName,
            body: {
                mappings: {
                    dynamic: 'strict', // Prevent dynamic mapping
                    properties: {
                        game_id: { type: 'integer' },
                        game_date: { type: 'date' },
                        player_id: { type: 'integer' },
                        player_full_name: { type: 'text' },
                        player_team_id: { type: 'integer' },
                        player_team_name: { type: 'text' },
                        home_team: { type: 'boolean' },
                        opponent_team_id: { type: 'integer' },
                        opponent_team_name: { type: 'text' },
                        points: { type: 'float' },
                        rebounds: { type: 'float' },
                        assists: { type: 'float' },
                        steals: { type: 'float' },
                        blocks: { type: 'float' },
                        fg_percentage: { type: 'float' },
                        minutes_played: { type: 'float' },
                    },
                },
            },
        });

        console.log('Index created:', response);
        return true;
    } catch (error) {
        console.error('Error creating index:', error);
        return false;
    }
}
</code></pre><p>4. Adicione a função para ingerir em massa os dados CSV no seu índice Elasticsearch. Dentro do bloco de código, omitimos a linha de cabeçalho. Em seguida, separe cada item da linha por vírgula e insira-os no objeto do documento. Esta etapa também os limpa e garante que sejam do tipo correto. Em seguida, inserimos os documentos na matriz bulkBody juntamente com as informações do índice, que servirão como carga útil para a ingestão em massa no Elasticsearch.</p><pre><code>async function bulkIngestCsv(filePath) {
    const readStream = fs.createReadStream(filePath);
    const rl = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity,
    });

    const bulkBody = [];
    let lineNum = 0;

    //Skip the header line
    let headerLine = true;
    for await (const line of rl) {
        if (headerLine) {
            headerLine = false;
            continue;
        }
        lineNum++;

        // Split the line by comma and remove whitespace
        const [
            game_id,
            game_date,
            player_id,
            player_full_name,
            player_team_id,
            player_team_name,
            home_team,
            opponent_team_id,
            opponent_team_name,
            points,
            rebounds,
            assists,
            steals,
            blocks,
            fg_percentage,
            minutes_played,
        ] = line.split(',');

        // Create a document object
        const document = {
            game_id: parseInt(game_id),
            game_date: game_date.trim(),
            player_id: parseInt(player_id),
            player_full_name: player_full_name.trim(),
            player_team_id: parseInt(player_team_id),
            player_team_name: player_team_name.trim(),
            home_team: home_team.trim() === 'True', // Converts True/False into a boolean
            opponent_team_id: parseInt(opponent_team_id),
            opponent_team_name: opponent_team_name.trim(),
            points: parseFloat(points),
            rebounds: parseFloat(rebounds),
            assists: parseFloat(assists),
            steals: parseFloat(steals),
            blocks: parseFloat(blocks),
            fg_percentage: parseFloat(fg_percentage),
            minutes_played: parseFloat(minutes_played),
        };

        // Prepare the bulk operation format
        bulkBody.push({ index: { _index: indexName } });
        bulkBody.push(document);
    }

    console.log(`Parsed ${lineNum} lines from CSV`);
</code></pre><p>5. Então, podemos usar <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">a API Bulk</a> do Elasticsearch com <code>elasticClient.bulk()</code> para ingerir vários documentos em uma única solicitação. O tratamento de erros abaixo está estruturado para fornecer uma contagem de quantos documentos não foram ingeridos e quantos foram ingeridos com sucesso.</p><pre><code>try {
        // Perform the bulk request
        const response = await elasticClient.bulk({ body: bulkBody });

        if (response.errors) {
            console.log('Bulk Ingestion had some hiccups:');

            // Count successful vs failed operations
            let successCount = 0;
            let errorCount = 0;
            const errorDetails = [];

            response.items.forEach((item, index) => {
                const operation = item.index || item.create || item.update || item.delete;
                if (operation.error) {
                    errorCount++;
                    errorDetails.push({
                        document: index + 1,
                        error: operation.error,
                    });
                } else {
                    successCount++;
                }
            });

            console.log(`Successfully indexed: ${successCount} documents`);
            console.log(`Failed to index: ${errorCount} documents, here are the details`, errorDetails);

        } else {
            console.log(`Bulk Ingestion fully successful!`);
        }

    } catch (error) {
        console.error('Error performing bulk ingestion:', error);
    }
}
</code></pre><p>6. Execute a função <code>main()</code> abaixo para executar sequencialmente as funções <code>createIndex()</code> e <code>bulkIngestCsv()</code> .</p><pre><code>// Run this function
async function main() {
    const result = await createIndex();
    if (!result) {
        console.error('Index setup failed. Aborting.');
        return;
    }

    await bulkIngestCsv(filePath);
    console.log('Bulk ingestion completed!');
}

main();
</code></pre><p>Se você vir um registro no console indicando que a ingestão em massa foi bem-sucedida, faça uma verificação rápida no seu índice do Elasticsearch para confirmar se os documentos foram realmente ingeridos com sucesso.</p><h4><strong>Etapa 5: Definindo e consolidando as agregações do Elasticsearch</strong></h4><p>Essas serão as principais funções que serão utilizadas quando definirmos as ferramentas para o Agente de IA, a fim de comparar as estatísticas dos jogadores entre si.</p><p>1. Navegue até o diretório <code>backend/lib</code> e crie um arquivo chamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticAggs.js">elasticAggs.js</a>.</p><p>2. Adicione a consulta abaixo para calcular as médias históricas de um jogador contra um adversário específico. Esta consulta usa um <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/full-text-search/filters">filtro</a> <code>bool</code> com 2 condições: uma que corresponde <code>player_id</code> e outra que corresponde a <code>opponent_team_id</code>, para recuperar apenas os jogos relevantes. Não precisamos retornar nenhum documento, só nos interessam as agregações, então definimos <code>size:0</code>. No bloco <code>aggs</code> , executamos várias <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agregações</a> de métricas em paralelo em campos como <code>points, rebounds, assists, steals, blocks</code> e <code>fg_percentage</code> para calcular seus valores médios. Os cálculos dos LLMs podem ser inconsistentes, e essa solução transfere esse processo para o Elasticsearch, garantindo que nosso assistente de IA da NBA tenha acesso a dados precisos.</p><pre><code>export async function getHistoricalAveragesAgainstOpponent(player_id, opponent_team_id) {
    try {
        //Query for Historical Averages
        const historicalQuery = await elasticClient.search({
            index: 'sample-nba-player-data', 
            size: 0,
            query: {
                bool: {
                    must: [
                        {
                            term: {
                                player_id: {
                                    value: player_id,
                                },
                            },
                        },
                        {
                            term: {
                                opponent_team_id: {
                                    value: opponent_team_id,
                                },
                            },
                        },
                    ],
                },
            },
            aggs: {
                avg_points: { avg: { field: 'points' } },
                avg_rebounds: { avg: { field: 'rebounds' } },
                avg_assists: { avg: { field: 'assists' } },
                avg_steals: { avg: { field: 'steals' } },
                avg_blocks: { avg: { field: 'blocks' } },
             avg_fg_percentage: { avg: { field: 'fg_percentage' } },
            },
        });

        return {
            points: historicalQuery.aggregations.avg_points.value || 0,
            rebounds: historicalQuery.aggregations.avg_rebounds.value || 0,
            assists: historicalQuery.aggregations.avg_assists.value || 0,
            steals: historicalQuery.aggregations.avg_steals.value || 0,
            blocks: historicalQuery.aggregations.avg_blocks.value || 0,
            fgPercentage: historicalQuery.aggregations.avg_fg_percentage.value || 0,
        };
    } catch (error) {
        console.error('Query error from getHistoricalAveragesAgainstOpponent function:', error);
        return { error: 'Queries failed in getting historical averages against opponent.' };
    }
}
</code></pre><p>3. Para calcular as médias da temporada de um jogador contra um adversário específico, usaremos praticamente a mesma consulta que a consulta histórica. A única diferença nesta consulta é que o filtro <code>bool</code> tem uma condição adicional para <code>game_date</code>. O campo <code>game_date</code> tem que estar dentro do intervalo da temporada atual da NBA. Neste caso, o intervalo está entre <code>2024-10-01</code> e <code>2025-06-30</code>. Essa condição adicional abaixo garante que as agregações subsequentes isolarão apenas os jogos desta temporada.<br/></p><pre><code>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
</code></pre><h4><strong>Etapa 6: Ferramenta de comparação de jogadores</strong></h4><p>Para manter nosso código modular e de fácil manutenção, criaremos um arquivo utilitário que consolida funções auxiliares de metadados e agregações do Elasticsearch. Isso alimentará a principal ferramenta usada pelo agente. Mais sobre isso adiante:</p><p>1. Crie um novo arquivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/comparePlayers.js">comparePlayers.js</a> no diretório <code>backend/lib</code> .</p><p>2. Adicione a função abaixo para consolidar os auxiliares de metadados e a lógica de agregação do Elasticsearch em uma única função que alimenta a ferramenta principal usada pelo agente.<br/></p><pre><code>import { playersByName } from '../data/playerAndTeamInfo.js';
import { teamsByName } from '../data/playerAndTeamInfo.js';
import { upcomingMatchups } from '../data/playerAndTeamInfo.js';
import { getHistoricalAveragesAgainstOpponent } from './elasticAggs.js';
import { getSeasonAveragesAgainstOpponent } from './elasticAggs.js';

//Simple helper functions to simulate API calls for player and team metadata. These reference the hardcoded values from playerAndTeamInfo.js in the data directory
export function getPlayerInfo(playerFullName) {
    return playersByName[playerFullName];
}

export function getTeamID(teamFullName) {
    return teamsByName[teamFullName];
}

export function getUpcomingMatchups(teamId) {
    return upcomingMatchups[teamId];
}

//Main function used by the 'playerComparisonTool' agent tool
export async function comparePlayersForNextMatchup(player1Name, player2Name) {
    //Get Player Info
    const player1Info = getPlayerInfo(player1Name);
    const player2Info = getPlayerInfo(player2Name);

    //Get upcoming matchups
    const player1NextGame = getUpcomingMatchups(player1Info.team_id)[0];
    const player2NextGame = getUpcomingMatchups(player2Info.team_id)[0];

    //Get season and historical averages against next opponent for player 1
    const player1SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );
    const player1HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );

    //Get season and historical averages against next opponent for player 2
    const player2SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );
    const player2HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );

    const player1 = {
        name: player1Name,
        playerId: player1Info.player_id,
        teamId: player1Info.team_id,
        nextOpponent: {
            teamId: player1NextGame.opponent_team_id,
            teamName: player1NextGame.opponent_team_name,
            home: player1NextGame.home,
        },
        stats: {
            seasonAverages: player1SeasonAverages,
            historicalAverages: player1HistoricalAverages,
        },
    };

    const player2 = {
        name: player2Name,
        playerId: player2Info.player_id,
        teamId: player2Info.team_id,
        nextOpponent: {
            teamId: player2NextGame.opponent_team_id,
            teamName: player2NextGame.opponent_team_name,
            home: player2NextGame.home,
        },
        stats: {
            seasonAverages: player2SeasonAverages,
            historicalAverages: player2HistoricalAverages,
        },
    };

    return [player1, player2];
}
</code></pre><h4><strong>Etapa 7: Construindo o agente</strong></h4><p>Agora que você criou a estrutura básica do frontend e do backend, importou os dados dos jogos da NBA e estabeleceu uma conexão com o Elasticsearch, podemos começar a juntar todas as peças para construir o agente.</p><p><strong>Definindo o agente</strong></p><p>1. Navegue até o arquivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/agents/index.ts">index.ts</a> dentro do diretório <code>backend/src/mastra/agents</code> e adicione a definição do agente. Você pode especificar campos como:</p><ul><li><strong>Nome:</strong> Dê ao seu agente um nome que será usado como referência quando ele for chamado na interface.</li><li><strong>Instruções/mensagem do sistema: </strong>Uma mensagem do sistema fornece ao LLM o contexto inicial e as regras a seguir durante a interação. É semelhante à mensagem que os usuários enviam pelo chat, mas esta é exibida antes de qualquer interação do usuário. Novamente, isso irá variar dependendo do modelo que você escolher.</li><li><strong>Modelo:</strong> Qual modelo de aprendizagem de linguagem (LLM) usar (o Mastra suporta modelos OpenAI, antrópicos, locais, etc.).</li><li><strong>Ferramentas:</strong> Uma lista de funções de ferramentas que o agente pode chamar.</li><li><strong>Memória:</strong> (Opcional) se quisermos que o agente se lembre do histórico da conversa, etc. Para simplificar, podemos começar sem memória persistente, embora o Mastra a suporte.</li></ul><p></p><pre><code>import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { playerComparisonTool } from '../tools';

export const basketballAgent = new Agent({
    name: 'Basketball Agent',
    instructions: `
      You are a NBA Basketball expert.
      Your primary function is to compare two NBA players and recommend which one is the better fantasy pickup.

      Only compare players from the following list:
      - LeBron James
      - Stephen Curry
      - Jayson Tatum
      - Jaylen Brown
      - Nikola Jokic
      - Luka Doncic
      - Kyrie Irving
      - Anthony Davis
      - Kawhi Leonard
      - Russell Westbrook

      Input Handling Rules:
      - If the user asks about a player that is not on this list, respond with the list of available players for comparison.
      - If the user only inputs one player, ask the user to add another player from the list provided.
      - If the user inputs a player with the wrong spelling or capitalizations, infer from the list of available players provided.
      - IMPORTANT: If the user asks a question or asks you to generate a response about anything outside of basketball or the scope of this project, DO NOT answer and affirm you can only talk about basketball.

      Tool Usage:
      - Extract and standardize player names to match the list exactly.
      - Use the playerComparisonTool, passing both names as strings.
      - The tool will return an object with game information, stats, and analysis.

      Format your response using Markdown syntax. Use:

        Example output format:

       
        #### Next Game Info
        - ***LeBron James** vs Warriors, May 24 (Home)  
        - ***Stephen Curry** vs Lakers, May 24 (Away)


        #### Stats Comparison  
        \`\`\`  
        Stat                  LeBron James (vs Warriors)    Stephen Curry (vs Lakers)  
        --------------------  -----------------------------  ----------------------------  
        Historical Points     28.3                          30.3  
        Historical Assists    6.7                           8.7  
        Season Points         28.8                          23.3  
        Season Assists        6.2                           4.7  
        \`\`\`

        #### Fantasy Recommendation  
        Explain which player is the better fantasy pickup and why.
      
    `,
    model: openai('gpt-4o'),
    tools: { playerComparisonTool },
});
</code></pre><p><strong><br/>Ferramentas de definição</strong></p><ol><li>Navegue até o arquivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/tools/index.ts">index.ts</a> dentro do diretório <code>backend/src/mastra/tools</code> .</li><li>Instale o Zod usando o comando:</li></ol><pre><code>npm install zod</code></pre><p>3. Adicionar definições de ferramentas. Observe que importamos a função dentro do arquivo <code>comparePlayers.js</code> como a função principal que o agente usará ao chamar esta ferramenta. Usando a função <code>createTool()</code> do Mastra, vamos registrar nosso <code>playerComparisonTool</code>. Os campos incluem:</p><ul><li><code>id</code>Esta é uma descrição em linguagem natural para ajudar o agente a entender o que a ferramenta faz.</li><li><code>input schema</code>Para definir o formato da entrada para a ferramenta, o Mastra utiliza o esquema <a href="https://zod.dev/">Zod</a> , que é uma biblioteca de validação de esquemas TypeScript. Zod ajuda garantindo que o agente insira dados estruturados corretamente e impede que a ferramenta seja executada caso a estrutura de entrada não corresponda.</li><li><code>description</code>Esta é uma descrição em linguagem natural para ajudar o agente a entender quando ligar e usar a ferramenta.</li><li><code>execute</code>A lógica que é executada quando a ferramenta é chamada. No nosso caso, estamos usando uma função auxiliar importada para retornar estatísticas de desempenho.</li></ul><pre><code>import { comparePlayersForNextMatchup } from '../../../lib/comparePlayers.js'
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const playerComparisonTool = createTool({
    id: "Compare two NBA players",
    inputSchema: z.object({
        player1:z.string(),
        player2:z.string()
    }),
    description: "Use this tool to compare two players given in the user prompt.",
    execute: async ({ context: { player1, player2 } }) => {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})</code></pre><p><strong>Adicionando middleware para lidar com CORS</strong></p><p>Adicione um middleware no servidor Mastra para lidar com <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">CORS</a>. Dizem que existem três coisas na vida que você não pode evitar: a morte, os impostos e, para desenvolvedores web, o CORS. Em resumo, o Compartilhamento de Recursos de Origem Cruzada (CORS) é um recurso de segurança do navegador que impede que o frontend faça solicitações a um backend executado em um domínio ou porta diferente. Embora executemos tanto o backend quanto o frontend em localhost, eles usam portas diferentes, acionando a política CORS. Precisamos adicionar o middleware especificado na <a href="https://mastra.ai/en/docs/server-db/middleware">documentação do Mastra</a> para que nosso backend permita essas solicitações do frontend.</p><p>1. Navegue até o arquivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/index.ts">index.ts</a> dentro do diretório <code>backend/src/mastra</code> e adicione a configuração para CORS:</p><ul><li><code>origin: [&#x27;http://localhost:5173&#x27;]</code><ul><li>Permite solicitações somente deste endereço (endereço padrão do Vite)</li></ul></li><li><code>allowMethods: [&quot;GET&quot;, &quot;POST&quot;]</code><ul><li>Métodos HTTP permitidos. Na maioria das vezes, será utilizado o método POST.</li></ul></li><li><code>allowHeaders: [&quot;Content-Type&quot;, &quot;Authorization&quot;, &quot;x-mastra-client-type, &quot;x-highlight-request&quot;, &quot;traceparent&quot;],</code><ul><li>Essas configurações definem quais cabeçalhos personalizados podem ser usados nas solicitações.</li></ul></li></ul><p></p><pre><code>import { Mastra } from '@mastra/core/mastra';
import { basketballAgent } from './agents';

console.log('Starting Mastra server...');

export const mastra = new Mastra({
  agents: { basketballAgent },
  server:{
    timeout: 10 * 60 * 1000, // 10 minutes
    cors: {
      origin: ['http://localhost:5173'],
      allowMethods: ["GET", "POST"],
      allowHeaders: [
        "Content-Type",
        "Authorization",
        "x-mastra-client-type",
        "x-highlight-request",
        "traceparent",
      ],
      exposeHeaders: ["Content-Length", "X-Requested-With"],
      credentials: false,
    },
  },

});

console.log('Mastra server configured.'); // Log after server configuration
</code></pre><h4><strong>Etapa 8: Integrando o frontend</strong></h4><p>Este componente React fornece uma interface de chat simples que se conecta ao agente de IA Mastra usando o gancho <a href="https://mastra.ai/en/docs/frameworks/agentic-uis/ai-sdk#using-the-usechat-hook">useChat()</a> de <code>@ai-sdk/react</code>. Também usaremos esse recurso para exibir o uso de tokens, chamadas de ferramentas e para renderizar a conversa. No prompt do sistema acima, também pedimos ao agente para exibir a resposta em markdown, então usaremos <code>react-markdown</code> para formatar a resposta corretamente.</p><p></p><p>1. No diretório frontend, instale o pacote @ai-sdk/react para usar o gancho useChat().</p><pre><code>npm install @ai-sdk/react</code></pre><p>2. Ainda no mesmo diretório, instale o React Markdown para que possamos formatar corretamente a resposta gerada pelo agente.</p><pre><code>npm install react-markdown</code></pre><p>3. Implemente <code>useChat()</code>. Este gancho gerenciará a interação entre seu frontend e o backend do seu agente de IA. Ele gerencia o estado das mensagens, a entrada do usuário, o status e fornece ganchos de ciclo de vida para fins de observabilidade. As opções que passamos incluem:</p><ul><li><code>api:</code> Isso define o ponto final do seu agente Mastra AI. A porta padrão é a 4111 e também queremos adicionar a rota que suporta respostas em fluxo contínuo.</li><li><code>onToolCall</code>Este comando é executado sempre que o agente chama uma ferramenta; estamos usando-o para rastrear quais ferramentas nosso agente está chamando.</li><li><code>onFinish</code>Esta ação é executada depois que o agente conclui uma resposta completa. Mesmo que tenhamos habilitado o streaming, <code>onFinish</code> ainda será executado após o recebimento da mensagem completa e não após cada parte. Aqui, estamos usando isso para rastrear o uso de nossos tokens. Isso pode ser útil para monitorar e otimizar os custos do LLM.</li></ul><p>4. Finalmente, acesse o componente <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/frontend/components/ChatUI.jsx">ChatUI.jsx</a> no diretório <code>frontend/components</code> para criar a interface do usuário para manter nossa conversa. Em seguida, envolva a resposta em um componente <code>ReactMarkdown</code> para formatar corretamente a resposta do agente.</p><pre><code>import React, { useState } from 'react';
import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

export default function ChatUI() {
    const [totalTokenUsage, setTotalTokenUsage] = useState(0);
    const [promptTokenUsage, setPromptTokenUsage] = useState(0);
    const [completionTokenUsage, setCompletionTokenUsage] = useState(0);
    const [toolsCalled, setToolsCalled] = useState([]);

    const { messages, input, handleInputChange, handleSubmit, status } = useChat({
        api: 'http://localhost:4111/api/agents/basketballAgent/stream', //Replace with your own endpoint for your agent
        id: 'my-chat-session',

        //Optional parameter to check agent tool calls
        onToolCall: ({ toolCall }) => {
            setToolsCalled((prev) => [...prev, toolCall.toolName]);
        },

        //Optional parameter to check token usages
        onFinish: (message, { usage }) => {
            setTotalTokenUsage((prev) => prev + usage.totalTokens);
            setPromptTokenUsage((prev) => prev + usage.promptTokens);
            setCompletionTokenUsage((prev) => prev + usage.completionTokens);
        },

        //Optional parameter for error handling
        onError: (error) => {
            console.error('Agent error:', error);
        },
    });

    return (
        <div>
            <div className="agent-info">
                <h4 className="stats-title">What's My Agent Doing?</h4>

                <div className="stats-box">
                    <strong className="stats-sub-title">Tools Called:</strong>
                    <ul className="tool-list">
                        {toolsCalled.map((tool, idx) => (
                            <li key={idx}>{tool}</li>
                        ))}
                        {toolsCalled.length === 0 && <li>No tools called yet.</li>}
                    </ul>

                    <div className="usage-stats">
                        <p>Prompt Token Usage: {promptTokenUsage}</p>
                        <p>Completion Token Usage: {completionTokenUsage}</p>
                        <p>Total Token Usage: {totalTokenUsage}</p>
                    </div>
                </div>
            </div>

            <strong>Conversation:</strong>
            <div className="convo-box">
                {messages.map((msg) => (
                    <div key={msg.id} className="message-item">
                        <strong className="message-role">{msg.role === 'assistant' ? 'Basketbot' : 'You'}:</strong>
                        <ReactMarkdown>{msg.content}</ReactMarkdown>
                    </div>
                ))}
            </div>

            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                />
                <button type="submit" disabled={status === 'streaming'}>
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                </button>
            </form>
        </div>
    );
}</code></pre><h4><strong>Etapa 9: Executando o aplicativo</strong></h4><p>Parabéns! Agora você está pronto para executar o aplicativo. Siga estes passos para iniciar tanto o backend quanto o frontend.</p><ol><li>Em uma janela de terminal, partindo do diretório raiz, navegue até o diretório de backend e inicie o servidor Mastra:</li></ol><pre><code>cd backend

npm run dev</code></pre><p>2. Em outra janela do terminal, partindo do diretório raiz, navegue até o diretório frontend e inicie o aplicativo React:</p><p></p><pre><code>cd frontend

npm run dev</code></pre><p></p><p>3. Acesse seu navegador e navegue até:</p><p></p><p><a href="http://localhost:5173/">http://localhost:5173</a></p><p></p><p>Você deverá conseguir visualizar a interface de bate-papo. Experimente estas sugestões:</p><ul><li>&quot;Compare LeBron James e Stephen Curry&quot;</li><li>&quot;Quem devo escolher entre Jayson Tatum e Luka Doncic?&quot;</li></ul><p></p><h3><strong>O que vem a seguir: tornar o agente mais inteligente.</strong></h3><p>Para tornar o assistente mais proativo e as recomendações mais relevantes, adicionarei algumas melhorias importantes na próxima versão.</p><p></p><p><strong>Busca semântica para notícias da NBA</strong></p><p>Existem inúmeros fatores que podem afetar o desempenho do jogador, muitos dos quais não aparecem nas estatísticas brutas. Informações como relatórios de lesões, alterações na escalação ou até mesmo análises pós-jogo só podem ser encontradas em artigos de notícias. Para capturar esse contexto adicional, adicionarei recursos de busca semântica para que o agente possa recuperar artigos relevantes da NBA e incorporar essa narrativa em suas recomendações.</p><p></p><p><strong>Pesquisa dinâmica com o servidor Elasticsearch MCP</strong></p><p>O MCP (Model Context Protocol) está rapidamente se tornando o padrão para a forma como os agentes se conectam às fontes de dados. Vou migrar a lógica de busca para o servidor Elasticsearch MCP, o que permite que o agente construa consultas dinamicamente em vez de depender de funções de busca predefinidas que fornecemos. Isso nos permite usar fluxos de trabalho em linguagem mais natural e reduz a necessidade de escrever manualmente cada consulta de pesquisa. Saiba mais sobre o servidor Elasticsearch MCP e o estado atual do ecossistema <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">aqui</a>.</p><p></p><p>Essas mudanças já estão em andamento, fique ligado!</p><h3><strong>Conclusão</strong></h3><p></p><p>Neste blog, criamos um assistente RAG interativo que fornece recomendações personalizadas para o seu time de basquete de fantasia usando JavaScript, Mastra e Elasticsearch. Nós abordamos os seguintes tópicos:</p><ul><li><strong>Fundamentos do RAG agético</strong> e como a combinação da autonomia de um agente de IA com as ferramentas para usar o RAG de forma eficaz pode levar a agentes mais dinâmicos e com nuances.</li><li><strong>Elasticsearch </strong>e como seus recursos de armazenamento de dados e poderosas agregações nativas o tornam um excelente parceiro como base de conhecimento para um mestrado em Direito (LLM).</li><li><strong>O framework Mastra </strong>e como ele simplifica a criação desses agentes para desenvolvedores no ecossistema JavaScript.</li></ul><p>Seja você um fanático por basquete, esteja explorando como construir agentes de IA, ou ambos como eu, espero que este blog tenha lhe dado algumas bases para começar. O repositório completo está disponível no <a href="https://github.com/jdarmada/nba-ai-assistant-js">GitHub</a>. Sinta-se à vontade para cloná-lo e fazer alterações. Agora, vá ganhar essa liga de fantasia!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scripting em Ruby no Logstash]]></title>
            <description><![CDATA[Saiba mais sobre o plugin de filtro Ruby para Logstash, que permite a transformação avançada de dados em seu pipeline Logstash.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/ruby-scripting-logstash</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/ruby-scripting-logstash</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Dai Sugimori]]></dc:creator>
            <pubDate>Tue, 24 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b1bcd63c033ccbde102c20ba3085f165f9289a71-1600x1000.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Logstash é um pipeline de processamento de dados que ingere dados de múltiplas fontes, os transforma e os envia para os destinos escolhidos. Os plugins de filtro são essenciais para esse processo; eles executam operações específicas nos seus dados à medida que estes percorrem o pipeline.</p><p>O Logstash inclui diversos filtros integrados para tarefas comuns, como analisar, enriquecer e modificar dados. Mas, às vezes, você encontrará cenários que exigem lógica personalizada que vai além do que esses filtros padrão podem fornecer. É aí que entra o <a href="https://www.elastic.co/docs/reference/logstash/plugins/plugins-filters-ruby">plugin de filtro Ruby</a> .</p><p><strong>O plugin de filtro Ruby permite executar código Ruby personalizado diretamente no seu pipeline Logstash.</strong> Quando os filtros padrão não são suficientes, o filtro Ruby permite lidar com transformações de dados complexas, implementar lógica de negócios personalizada ou integrar-se a sistemas externos.</p><p>Neste blog, vamos explorar como usar filtros em Ruby, desde o uso básico até o avançado.</p><h2>Quando você deve usar o filtro Ruby?</h2><p>Como arquiteto consultor da Elastic, vejo frequentemente clientes usando o Logstash para o pipeline de processamento de dados, embora atualmente ele não seja um mecanismo de processamento de dados de última geração. Eles frequentemente enfrentam dificuldades com as limitações dos filtros padrão quando se trata de manipulação complexa de dados ou lógica personalizada. Nesses casos, o filtro Ruby pode ajudar a superar esses desafios.</p><p>O filtro Ruby é útil quando os filtros padrão do Logstash não atendem às suas necessidades específicas. Aqui estão alguns casos de uso comuns:</p><ul><li><strong>Manipulação de dados aninhados em profundidade</strong>: Modifique estruturas JSON complexas, arrays dentro de arrays ou reestruture dados dinamicamente com base no conteúdo.</li><li><strong>Processamento avançado de strings</strong>: Analise e extraia dados estruturados de textos não estruturados.</li><li><strong>Implementação de lógica de negócios complexa</strong>: Crie transformações personalizadas que exigem lógica condicional, loops ou cálculos complexos.</li></ul><h2>Uso básico</h2><p>Vamos começar com um exemplo simples para entender como funciona o filtro Ruby.</p><h3>Configurando o filtro Ruby</h3><p>Ao criar um pipeline Logstash, você deve colocar o arquivo de configuração no diretório <code>/etc/logstash/conf.d</code> . Alternativamente, você pode usar a opção <code>-f</code> para especificar o caminho para o arquivo de configuração ao iniciar o Logstash manualmente, para que você possa experimentar seus pipelines facilmente.</p><pre><code>$ ./bin/logstash -f /path/to/your_pipeline.conf</code></pre><p>O arquivo de configuração deve ter a extensão <code>.conf</code> .</p><p>Para usar o filtro Ruby, defina um filtro <code>ruby</code> na seção de filtro do seu arquivo de configuração do pipeline Logstash (*.conf). Eis um exemplo básico:</p><pre><code>filter {
  ruby {
    code => "
      event.set('new_field', 'Hello from Ruby!')
    "
  }
}</code></pre><p>Este filtro Ruby embutido define uma instância de filtro Ruby dentro da sua configuração do Logstash. O parâmetro <code>code</code> fornece o script Ruby embutido que o Logstash executará para cada evento processado por este filtro. Dentro desse script, há uma variável <code>event</code> disponível que representa o próprio evento. O objeto de evento contém os dados originais enviados ao Logstash e quaisquer campos adicionais criados durante os estágios de filtragem do Logstash. Você pode acessar esses campos por meio da API de Eventos do Logstash, como <code>event.get()</code> e <code>event.set()</code>. Neste exemplo de código, <code>event.set(&#x27;new_field&#x27;, &#x27;Hello from Ruby!&#x27;)</code> definiu um novo campo chamado <code>new_field</code> com o valor da string <code>Hello from Ruby!</code>. Você pode adicionar qualquer outro código neste bloco <code>code</code> conforme necessário.</p><p>Note que este objeto <code>event</code> não é um objeto hash comum do Ruby, embora funcione como um contêiner de dados do tipo chave-valor. Consulte <a href="https://www.elastic.co/docs/reference/logstash/event-api">esta documentação oficial</a> para saber mais sobre a API de Eventos.</p><h3>Externalizar script Ruby</h3><p>Para transformações simples, o código Ruby embutido é conveniente. Porém, para lógica complexa ou funções reutilizáveis, recomenda-se mover o código para um script Ruby externo. Isso melhora a capacidade de manutenção e mantém a configuração do seu pipeline Logstash organizada.</p><p>Primeiro, crie um script Ruby e salve-o como <code>my_ruby_script.rb</code>. O script deve definir um método <code>filter</code> que processa o evento. Ela recebe um objeto de evento como argumento, que representa o evento atual que está sendo processado. O método <code>filter</code> precisa retornar uma matriz de eventos para emitir. Para descartar o evento, retorne um array vazio.</p><p>Por exemplo, o seguinte script lê o campo <code>message</code> , calcula seu comprimento e armazena o resultado em um novo campo chamado <code>message_length</code>.</p><pre><code>def register(params)
  # This method is called when the plugin is loaded.
  # You can use it to initialize any instance variables or perform setup tasks.
end

def filter(event)
  message = event.get('message')

  if message
    event.set('message_length', message.length)
  end

  return [event]
end</code></pre><p>Em seguida, defina a configuração do filtro Ruby para referenciar o script usando a opção <code>path</code> . Isso instrui o Logstash a carregar e executar o script externo. Ao usar scripts externos, certifique-se de que o arquivo existe e possui as permissões corretas.</p><pre><code>filter {
  ruby {
    path => "/path/to/my_ruby_script.rb"
  }
}</code></pre><p>Agora, cada evento é passado para o método <code>filter</code> em <code>my_ruby_script.rb</code> e é processado por ele.</p><p>Essa abordagem ajuda você a gerenciar lógicas complexas com mais eficiência, facilitando o teste, a depuração e a reutilização do seu código Ruby.</p><h2>Uso avançado</h2><p>Nesta seção, exploraremos alguns exemplos avançados de uso do filtro Ruby no Logstash. Estes exemplos demonstrarão como realizar transformações de dados, enriquecer eventos e implementar lógica personalizada usando Ruby.</p><h3>Manipulando estruturas de dados aninhadas</h3><p>Um evento do Logstash é a estrutura de dados principal que o Logstash processa. Pode conter diversos campos, incluindo estruturas de dados aninhadas, como arrays e hashes. O filtro Ruby permite manipular essas estruturas aninhadas com facilidade.</p><p>O filtro Ruby consegue lidar com estruturas de dados aninhadas, como hashes e arrays, permitindo que você modifique ou adicione campos dentro dessas estruturas. Isso é útil ao lidar com formatos de dados complexos como JSON.</p><pre><code>input {
  generator {
    lines => [
      '{"nested": {"key1": "value1", "key2": "value2"}}'
    ]
    count => 1
    codec => "json"
    ecs_compatibility => "disabled"
  }
}

filter {
  ruby {
    code => "
      nested_data = event.get('nested')

      if nested_data.is_a?(Hash)
        nested_data['key3'] = 'value3'
        event.set('nested', nested_data)
      end
    "
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Este exemplo inclui um objeto JSON aninhado nos dados de entrada. O filtro Ruby modifica os dados aninhados adicionando um novo par chave-valor. Esse tipo de manipulação de dados aninhados não é possível com os filtros padrão do Logstash, o que torna o filtro Ruby uma opção prática para estruturas de dados complexas.</p><h3>Dividir um único evento em vários eventos.</h3><p>Os filtros Ruby também podem ser usados para dividir um único evento em vários eventos. Isso é útil quando você tem um único evento contendo uma matriz de itens e deseja criar eventos separados para cada item.</p><p>Note que nem o pipeline de ingestão do Elasticsearch nem os processadores do Beats/Elastic Agent suportam a divisão de eventos. Este é um dos casos de uso mais fortes para o Logstash.</p><h4>Com filtro dividido</h4><p>Você pode usar o filtro <code>split</code> para dividir um evento em vários eventos com base em um campo especificado. No entanto, se precisar realizar transformações ou lógicas adicionais durante a divisão, você pode usar o filtro Ruby em combinação com o filtro de divisão.</p><p>No exemplo a seguir, temos um feed RSS como uma única linha de texto XML. Contém múltiplos elementos <code>&lt;item&gt;</code> . O filtro Ruby é usado para extrair os elementos <code>&lt;item&gt;</code> do XML e armazená-los em um novo campo chamado <code>items</code>. O filtro de divisão é então usado para dividir o evento em vários eventos com base no campo <code>items</code> .</p><pre><code>input {
  generator {
    lines => [
      '<rss version="2.0"><channel><title>Sample RSS</title><item><title>Article 1</title><link>http://example.com/1</link><description>Desc 1</description></item><item><title>Article 2</title><link>http://example.com/2</link><description>Desc 2</description></item></channel></rss>'
    ]
    count => 1
    codec => "plain"
    ecs_compatibility => "disabled"
  }
}

filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    code => "event.set('items', event.get('[rss][channel][item]')) if event.get('[rss][channel][item]')"
  }
  split {
    field => "items"
  }
  ruby {
    code => "
      item = event.get('items')
      event.set('title', item['title']) if item['title']
      event.set('link', item['link']) if item['link']
      event.set('description', item['description']) if item['description']
    "
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Isso resultará no seguinte:</p><pre><code>{
          "title" => "Article 1",
           "link" => "http://example.com/1",
    "description" => "Desc 1"
}
{
          "title" => "Article 2",
           "link" => "http://example.com/2",
    "description" => "Desc 2"
}</code></pre><p>Como você deve ter percebido, o filtro <code>ruby</code> não é essencial neste caso. O filtro <code>split</code> pode ser usado para dividir o evento em vários eventos com base no campo <code>items</code> e o filtro <code>mutate</code> pode ser usado para remover campos desnecessários. No entanto, se você precisar realizar transformações ou lógicas adicionais durante a divisão, poderá usar o filtro Ruby.</p><h4>Use script Ruby embutido</h4><p>Você também pode usar um script Ruby embutido para dividir um único evento em vários eventos usando o método <code>event.clone</code> e o <code>new_event_block variable</code>, como <code>new_event_block.call(new_event)</code>. Isso permite criar novos eventos com base no evento original, preservando seus dados.</p><p>Aqui está um exemplo de como usar o filtro Ruby para dividir um único evento em vários eventos. A entrada e a saída são as mesmas do exemplo anterior.</p><pre><code>filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    code => "
      items = event.get('[rss][channel][item]')
      if items.is_a?(Array)
        items.each do |item|
          new_event = event.clone
          new_event.set('title', item['title'])
          new_event.set('link', item['link'])
          new_event.set('description', item['description'])
          new_event_block.call new_event
        end
        event.cancel
      elsif items.is_a?(Hash)
        event.set('title', items['title'])
        event.set('link', items['link'])
        event.set('description', items['description'])
      end
    "
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}</code></pre><h4>Utilizar script Ruby externo</h4><p>Você também pode usar um script Ruby externo para dividir um único evento em vários eventos.</p><p>Arquivo de configuração:</p><pre><code>filter {
  xml {
    source => "message"
    target => "rss"
    store_xml => true
    force_array => false
  }
  ruby {
    path => "path/to/ruby/split_event.rb"
  }
  mutate {
    remove_field => ["@timestamp", "@version", "sequence", "host", "event", "message", "rss", "items"]
  }
}</code></pre><p>O script Ruby precisa ser externalizado como <code>split_event.rb</code>:</p><pre><code>def filter(event)
  items = event.get('[rss][channel][item]')
  events = []
  if items.is_a?(Array)
    items.each do |item|
      new_event = event.clone
      new_event.set('title', item['title'])
      new_event.set('link', item['link'])
      new_event.set('description', item['description'])
      events << new_event
    end
    return events
  elsif items.is_a?(Hash)
    event.set('title', items['title'])
    event.set('link', items['link'])
    event.set('description', items['description'])
    return [event]
  else
    return []
  end
end</code></pre><p>Lembre-se, o método <code>filter</code> deve retornar uma matriz de eventos. Você pode retornar vários eventos clonando um objeto de evento recebido e adicionando-os à matriz, ou pode retornar um único evento como uma matriz com um único elemento.</p><pre><code>return events
# or
# return [event]</code></pre><p>Isso permite dividir um único evento em vários eventos.</p><h3>Executar comandos externos e analisar seus resultados.</h3><p>O plugin Logstash exec input permite executar comandos externos, e a saída desses comandos será um evento do Logstash. O resultado do comando será armazenado no campo <code>message</code> do evento.</p><p>Normalmente, a saída dos comandos do sistema é legível para humanos, mas não está estruturada em JSON ou outros formatos que o Logstash possa analisar facilmente. Para lidar com isso, você pode usar o filtro Ruby para analisar a saída e extrair as informações dela.</p><p>Aqui está um exemplo de uso do plugin de entrada <code>exec</code> para executar o comando <code>ps -ef</code> , que lista todos os processos em execução em um sistema do tipo Unix. A saída será analisada pelo filtro Ruby para extrair informações relevantes sobre cada processo.</p><pre><code>input {
  exec {
    command => "ps -ef"
    interval => 60
  }
}

filter {
  ruby {
    code => '
      processes = []
      lines = event.get("message").split("\n")  
      lines.each_with_index do |line, index|
        # Skip header line and empty lines
        next if index == 0 || line.strip.empty?
        entry = nil
        
        # Use regex to match the ps -ef output format more flexibly
        # This pattern accounts for variable spacing and different time formats
        if line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([\d:]+\.?\d*)\s+(.+)$/
          uid, pid, ppid, c, stime, tty, time, cmd = $1, $2, $3, $4, $5, $6, $7, $8
          
          entry = {
            "UID" => uid,
            "PID" => pid,
            "PPID" => ppid,
            "C" => c,
            "STIME" => stime,
            "TTY" => tty,
            "TIME" => time,
            "CMD" => cmd.strip
          }
        elsif line =~ /^\s*(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/
          # Fallback pattern for lines that might not match the exact format
          # Split the remaining part more carefully
          uid, pid, ppid, c, remainder = $1, $2, $3, $4, $5
          
          # Split remainder into STIME, TTY, TIME, CMD
          parts = remainder.strip.split(/\s+/, 4)
          if parts.length >= 4
            stime, tty, time, cmd = parts[0], parts[1], parts[2], parts[3]
            
            entry = {
              "UID" => uid,
              "PID" => pid,
              "PPID" => ppid,
              "C" => c,
              "STIME" => stime,
              "TTY" => tty,
              "TIME" => time,
              "CMD" => cmd
            }
          end
        end
        if entry && entry["UID"] == "0"
          original_line = line.strip
          entry["original_line"] = original_line if original_line.length > 0
          processes.push(entry)
        end
      end
      event.set("processes", processes)
      event.remove("message")
      event.remove("event")
    '
  }
}

output {
  stdout { codec => rubydebug }
}</code></pre><p>Este exemplo usa o plugin de entrada <code>exec</code> para executar o comando <code>ps -ef</code> a cada 60 segundos. O filtro Ruby processa a saída, extraindo campos relevantes como UID, PID, PPID, uso da CPU (C), hora de início (STIME), TTY, tempo total da CPU (TIME) e o comando (CMD) executado. Funciona bem no meu ambiente macOS, mas você pode precisar ajustar os padrões regex para corresponder ao formato de saída do comando <code>ps -ef</code> no seu sistema.</p><h3>Utilize bibliotecas integradas</h3><p>O plugin de filtro Ruby permite usar bibliotecas Ruby integradas, o que pode ser muito útil para diversas tarefas. Por exemplo, você pode usar a biblioteca <code>json</code> para analisar strings JSON ou a biblioteca <code>date</code> para manipular datas.</p><p>Aqui está um exemplo de como usar a biblioteca <code>json</code> para analisar uma string JSON armazenada em um campo:</p><pre><code>require 'json'

def filter(event)
  json_string = event.get('message')
  parsed_json = JSON.parse(json_string)
  event.set('parsed_json', parsed_json)
  return [event]
end</code></pre><p>Para evitar exigir a biblioteca sempre, você deve externalizar seu código Ruby para que possa usar a instrução <code>require</code> no início do seu script de filtro Ruby. Isso carregará a biblioteca uma única vez e a tornará disponível para uso em seu script.</p><p>Para verificar quais bibliotecas estão disponíveis em seu ambiente, você pode listar as bibliotecas integradas executando o seguinte código no filtro Ruby:</p><pre><code>Gem.loaded_specs.sort_by { |name, _| name }.each do |name, spec|
  puts "#{name}: #{spec.version}"
end</code></pre><p><strong>Observação: </strong>as bibliotecas integradas não são oficialmente suportadas pelo Logstash e seu comportamento pode mudar ou elas podem não estar disponíveis em versões futuras. Use-os por sua conta e risco.</p><h2>Conclusão</h2><p>O filtro Logstash Ruby permite personalizar e ampliar as funcionalidades dos seus pipelines Logstash. Neste post, abordamos os conceitos básicos do uso do filtro Ruby e fornecemos exemplos de uso avançado.</p><p>Ao utilizar o filtro Ruby, você pode lidar com tarefas complexas de processamento de dados que exigem lógica personalizada ou manipulação avançada. Seja trabalhando com estruturas de dados aninhadas, dividindo eventos ou analisando e convertendo texto complexo/não estruturado em JSON estruturado, o filtro Ruby oferece a flexibilidade necessária para atender às suas necessidades específicas.</p><p>Esperamos que este guia tenha lhe fornecido o conhecimento e a inspiração necessários para explorar todo o potencial do filtro Logstash Ruby. Boa programação!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como implantar o Elasticsearch no GCP GKE Autopilot]]></title>
            <description><![CDATA[Aprenda como implantar um cluster Elasticsearch no GCP usando o GKE Autopilot e o ECK para uma configuração parcialmente gerenciada do Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/eck-gke-autopilot</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/eck-gke-autopilot</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Eduard Martin]]></dc:creator>
            <pubDate>Thu, 19 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d9c462fe63011356671479ccfedd435eec1ede52-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, vamos aprender como implantar o Elasticsearch no Google Cloud Kubernetes (GKE) usando o Autopilot.</p><p>Para o Elasticsearch, usaremos <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes </a>(ECK), que é o operador oficial do Elasticsearch para Kubernetes e simplifica a orquestração de implantações do Kubernetes de todos os componentes do Elastic Stack.</p><p>Para saber mais sobre como implantar clusters do Elasticsearch em diferentes infraestruturas do GCP, você pode ler nossos artigos de introdução ao <a href="https://www.elastic.co/search-labs/blog/elasticsearch-gpc-google-compute-engine">Google Cloud Compute</a> e <a href="https://www.elastic.co/search-labs/blog/deploy-elastic-gcp-marketplace">Google Cloud Marketplace</a>.</p><h2>Esforço de implantação do Elasticsearch</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/56b54d718dcff9af9050873c41fdf738074851da-1428x582.png" style="width: 100%;" /><h3>O que é o GKE Autopilot?</h3><p>O <a href="https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview?hl=es-419"><strong>Google Kubernetes Engine (GKE) Autopilot</strong></a> oferece uma experiência Kubernetes totalmente gerenciada, na qual o Google cuida da configuração do cluster, do gerenciamento de nós, da segurança e do redimensionamento, enquanto os desenvolvedores se concentram na implantação de aplicativos, permitindo que as equipes passem do código à produção em minutos com as práticas recomendadas integradas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/bbf6de63fa0a199326352f521cb22654818799f6-1600x958.png" style="width: 100%;" /><h2>Quando usar o ECK no Google Cloud?</h2><p>O Elastic Cloud on Kubernetes (ECK) é mais indicado para organizações com infraestrutura Kubernetes existente que desejam implantar o Elasticsearch com recursos avançados, como funções dedicadas para nós, alta disponibilidade e automação.</p><h2>Como configurar o ECK na Google Cloud?</h2><p>1. Faça login no <a href="https://console.cloud.google.com">Google Cloud Console</a>.</p><p>2. No <strong>canto superior direito </strong>, clique<strong> </strong>no botão <strong>Cloud Shell</strong> para acessar o console e implantar o cluster GKE a partir daí. Alternativamente, você pode usar o <a href="https://cloud.google.com/cli">gcloud CLI</a>.</p><p><strong><em>Lembre-se de atualizar o id do projeto com o seu durante o tutorial.</em></strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/999b03861d4fe44f360ab4c7e2616e1dc10cf182-1558x1248.png" style="width: 100%;" /><p>3. Ative a <a href="https://console.cloud.google.com/flows/enableapi?apiid=container.googleapis.com">Google Kubernetes Engine API</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03827d3dc0e987c019e7747d33e7c01920047beb-911x246.png" style="width: 100%;" /><p>Clique em <strong><em>Avançar.</em></strong></p><p>Agora, a API do Kubernetes Engine deve aparecer como habilitada ao pesquisar pela API do Kubernetes Engine.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5cd46f0333086bcb31b80cf9c08a469b449ec0f-640x250.png" style="width: 100%;" /><p>4. No Cloud Shell, crie um cluster Autopilot. Vamos chamá-lo de autopilot-cluster-1 e também substituir autopilot-test pelo id do seu projeto.</p><pre><code>gcloud beta container --project "autopilot-test-457216" clusters create-auto "autopilot-cluster-1" --region "us-central1" --release-channel "regular" --tier "standard" --enable-ip-access --no-enable-google-cloud-access --network "projects/autopilot-test-457216/global/networks/default" --subnetwork "projects/autopilot-test-457216/regions/us-central1/subnetworks/default" --cluster-ipv4-cidr "/17" --binauthz-evaluation-mode=DISABLED</code></pre><p>5. Espere até que esteja pronto. Leva cerca de 10 minutos para ser criado.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/81fbc45ba56d0f16ba42724cb8ae45e60b327dbc-1581x258.png" style="width: 100%;" /><p>Uma mensagem de confirmação será exibida após a configuração correta do cluster.</p><p>6. Configure o acesso na linha de comando do kubectl.</p><pre><code>gcloud container clusters get-credentials autopilot-cluster-1 --region us-central1 --project autopilot-test-457216</code></pre><p>Você deverá ver:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b0beba1ee00ce9029f586ee32693fc2aa58c7f65-3442x142.png" style="width: 100%;" /><p><em>Entrada kubeconfig gerada para autopilot-cluster-1.</em></p><p>7. Instale o operador <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes</a> (ECK).</p><pre><code># Install ECK Custom Resource Definitions
kubectl create -f https://download.elastic.co/downloads/eck/2.16.1/crds.yaml

# Install the ECK operator
kubectl apply -f https://download.elastic.co/downloads/eck/2.16.1/operator.yaml</code></pre><p>8. Vamos criar um nó único do Elasticsearch com os valores padrão.</p><p>Se quiser conferir algumas receitas de diferentes instalações, você pode visitar <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/recipes">este link</a>.</p><p>Lembre-se de que, se você não especificar um <code>storageClass</code>, o ECK usará o definido por padrão, que para o GKE é <code>standard-rwo</code>, que usa o <a href="https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver?cloudshell=true">driver CSI de disco persistente do Compute Engine</a>, e criará um volume de 1 GB com ele.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
EOF</code></pre><p>Desativamos <code>nmap</code> porque a máquina padrão do GKE tem um valor de <code>vm.max_map_count</code> muito baixo. Desabilitá-lo não é recomendado para produção, mas aumenta o valor da <code>vm.max_map_count</code>. Você pode ler mais sobre como fazer isso <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/virtual-memory">aqui</a>.</p><p>9. Vamos também implantar um cluster Kibana de nó único. Para o Kibana, vamos adicionar um LoadBalancer que nos dará um IP externo que podemos usar para acessar o Kibana a partir do nosso dispositivo.</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      metadata:
        annotations:
          cloud.google.com/l4-rbs: "enabled"
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
EOF</code></pre><p>Observe a anotação: </p><p><code>cloud.google.com/l4-rbs: &quot;enabled&quot;</code></p><p><strong><em>É muito importante porque diz ao Autopilot que forneça um LoadBalancer voltado para o público. Se não estiver ativado, o LoadBalancer será interno.</em></strong></p><p>10. Verifique se seus pods estão funcionando.</p><pre><code>kubectl get pods</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99da1270581a137683770efdb9c6e1577ec9fc01-3150x442.png" style="width: 100%;" /><p>11. Você também pode <code>run kubectl get elasticsearch</code> e <code>kubectl get kibana</code> para obter estatísticas mais específicas, como versão, nós e integridade do Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/86234f307970fd5f78b8acd41496e8cc89ff82d3-3414x326.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/29160286ccc88928734c8ea11b1923db8e85d49d-3142x318.png" style="width: 100%;" /><p>12. Acesse seus serviços.</p><pre><code>kubectl get svc</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ed1a07314b84a99b4aa1fec3db4b9badeb9587ee-3446x610.png" style="width: 100%;" /><p>Isso mostrará o URL externo do Kibana em EXTERNAL-IP. Pode levar alguns minutos para a provisão do LoadBalancer. <strong><em>Copie o valor de EXTERNAL-IP.</em></strong></p><p>13 Obtenha a senha do Elasticsearch para o usuário &#x27;elastic&#x27;:</p><pre><code>kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' | base64 --decode</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/ab5054219216ebc15fc0d96e27605aaf13b720c6-3448x210.png" style="width: 100%;" /><p>14. <strong>Acesse o Kibana</strong> por meio do seu navegador:</p><ul><li>URL: https://&lt;EXTERNAL_IP&gt;:5601</li><li>Username:elastic</li><li>Senha: 28Pao50lr2GpyguX470L2uj5 (do passo anterior)</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/47cbe88dc14db64db3a256f3f7504cc86a843475-463x503.png" style="width: 100%;" /><p>15. Acessando a partir do seu navegador, você verá a tela de boas-vindas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a75c151c0144b7efe2b730698c0ed0156fa9b16a-1600x1005.png" style="width: 100%;" /><p>Se você quiser alterar as especificações do cluster do Elasticsearch, como mudar ou redimensionar nós, pode aplicar o manifesto yml novamente com as novas configurações:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
spec:
  version: 9.0.0
  nodeSets:
    - name: default
      count: 2
      config:
        node.store.allow_mmap: false
      podTemplate:
        spec:
          containers:
            - name: elasticsearch
              resources:
                requests:
                  memory: 1.5Gi
                  cpu: 2
                limits:
                  memory: 1.5Gi
                  cpu: 2
EOF</code></pre><p>Neste exemplo, adicionaremos mais um nó e modificaremos a RAM e a CPU. Como você pode ver, agora <code>kubectl get elasticsearch</code> mostra 2 nós:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/48b475622cc48890bff8105d151f2cbde28d7021-3418x298.png" style="width: 100%;" /><p>O mesmo vale para Kibana:</p><pre><code>cat <<EOF | kubectl apply -f -
apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
spec:
  version: 9.0.0
  http:
    service:
      metadata:
        annotations:
          cloud.google.com/l4-rbs: "enabled"
      spec:
        type: LoadBalancer
  count: 1
  elasticsearchRef:
    name: quickstart
  podTemplate:
    spec:
      containers:
        - name: kibana
          env:
            - name: NODE_OPTIONS
              value: "--max-old-space-size=1024"
          resources:
            requests:
              memory: 0.5Gi
              cpu: 0.5
            limits:
              memory: 1Gi
              cpu: 1
EOF</code></pre><p>Podemos ajustar o CPU/RAM do container e também o uso de memória do <a href="https://nodejs.org/">Node.js </a>(<a href="https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-mib">max-old-space-size</a>).</p><p>Lembre-se de que <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s/volume-claim-templates">as reivindicações de volume existentes não podem ser reduzidas</a>. Após aplicar a atualização, o operador fará as alterações com o menor tempo de interrupção.</p><p>Lembre-se de excluir o cluster quando terminar os testes para evitar custos desnecessários.</p><pre><code>gcloud container clusters delete autopilot-cluster-1</code></pre><h2>Próximas etapas</h2><p>Se você quiser saber mais sobre o Kubernetes e o Google Kubernetes Engine, consulte estes artigos:</p><ul><li><a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-on-k8s">Elastic Cloud on Kubernetes | Elastic Docs</a></li><li><a href="https://cloud.google.com/blog/products/containers-kubernetes/introducing-gke-autopilot">Apresentando o GKE Autopilot | Blog do Google Cloud</a></li><li><a href="https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-overview">Visão geral do Autopilot | Google Kubernetes Engine (GKE)</a></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Usando funções do Azure LLM com o Elasticsearch para experiências de consulta mais inteligentes]]></title>
            <description><![CDATA[Explore um exemplo de aplicativo de busca imobiliária que usa as Funções LLM do Azure Gen AI com o Elasticsearch para fornecer resultados de busca híbridos e flexíveis. Veja passo a passo como configurar e executar o aplicativo de exemplo no GitHub Codespaces.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/azure-llm-functions-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/azure-llm-functions-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
            <pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1431b90c7e00de06574c1e33c44a2e89296c824e-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Precisão. Quando importa, importa muito. Quando você está procurando por algo específico, a precisão é muito importante. No entanto, às vezes uma consulta muito precisa não retorna resultados, por isso é vantajoso ter a flexibilidade de ampliar o escopo de uma consulta para encontrar dados adicionais potencialmente relevantes.</p><p>Esta postagem do blog aborda o uso do Elasticsearch e do Azure Open AI para criar um aplicativo de exemplo que demonstra como encontrar resultados exatos ao pesquisar por propriedades imobiliárias muito específicas, ao mesmo tempo em que fornece resultados relevantes quando uma correspondência específica não está disponível. Percorreremos todas as etapas necessárias para criar um índice do Elasticsearch junto com um modelo de pesquisa. Em seguida, descreveremos todo o processo de como criar um aplicativo que usa o Azure OpenAI para receber consultas de usuários e transformá-las em consultas de modelo de pesquisa do Elasticsearch que podem produzir resultados incrivelmente personalizados.</p><p>Aqui está uma lista de todos os recursos que usaremos para criar nosso aplicativo de exemplo de busca de imóveis:</p><ul><li>Índice e modelo de pesquisa do Elasticsearch</li><li>Azure OpenAI</li><li>API do Azure Maps</li><li><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb">Caderno Jupyter de espaços de código</a></li><li>Kernel Semântico</li><li>Aplicativo C# com frontend Blazor</li></ul><h2>Fluxo de trabalho de consulta inteligente</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0c4a7c835e06c514f158c00ab1055a7ba719a35f-1600x765.png" style="width: 100%;" /><p>Este fluxo de trabalho combina LLM, ferramentas LLM e pesquisa para transformar consultas de linguagem natural em resultados de pesquisa estruturados e relevantes:</p><ul><li><strong>LLM (Large Language Model)</strong> - Interpreta consultas complexas do usuário e orquestra ferramentas para extrair intenção de pesquisa e enriquecer o contexto.</li><li><strong>Ferramentas LLM</strong> - Cada ferramenta LLM é um programa em C# que criamos para este post. Existem três ferramentas:<ul><li><em>Ferramenta de extração de parâmetros</em>: extrai atributos-chave como quartos, banheiros, recursos e preço da consulta.</li><li><em>Ferramenta GeoCode</em>: converte nomes de locais em latitude/longitude para filtragem espacial.</li><li><em>Ferramenta de pesquisa</em>: preenche um modelo de pesquisa do Elasticsearch com parâmetros de consulta e executa uma pesquisa. <strong>Pesquisa híbrida</strong> - Executa pesquisa híbrida (texto completo + vetor denso) com inferência de ML integrada. Essa abordagem em camadas garante experiências de consulta mais inteligentes e sensíveis ao contexto para o usuário final.</li></ul></li></ul><h2>Arquitetura de aplicação</h2><p>Aqui está um diagrama de arquitetura do sistema do aplicativo de exemplo. Usaremos um Jupyter Notebook do Codespaces para interagir com o Elastic Cloud.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2880afee184cd9270c0eb4310e51418e2339784d-936x452.png" style="width: 100%;" /><h2>Pré-requisitos</h2><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><h2>Clonar repositório de aplicativo de exemplo no Codespaces</h2><p>Comece clonando o código do aplicativo de exemplo. Você pode fazer isso no <a href="https://github.com/codespaces/">GitHub Codespaces</a>, que fornece uma maneira de clonar e executar aplicativos. Clique em <strong>Novo Codespace.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b89cbec491659b6c8a0bb9551ed2629f7a37f9fd-1600x427.png" style="width: 100%;" /><p>Em seguida, selecione o repositório <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo">jwilliams-elastic/msbuild-intelligent-query-demo</a> no menu suspenso <strong>Repositório</strong> e clique em <strong>Criar Codespace</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/43ea377554527af9578400f16cd2342bf8fff3a2-1600x1049.png" style="width: 100%;" /><h2>Criar .env arquivo</h2><p>Usaremos um Python Jupyter Notebook para acessar e interagir com o Elastic Cloud, e ele faz isso usando os valores de configuração armazenados em um arquivo de configuração. O arquivo de configuração do Notebook deve ter o nome de arquivo <strong><em>.env</em></strong> e você irá criá-lo agora.</p><ol><li>No GitHub Codespaces, clique no botão <strong>Novo arquivo </strong>e adicione um arquivo chamado <strong><em>.env</em></strong></li><li>Adicione o seguinte conteúdo ao <strong><em>.env</em></strong> recém-criado arquivo</li></ol><pre><code>ELASTIC_URL=
ELASTIC_API_KEY=</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/84d4f327948858ba61db0001dd8cf780d42fe0a7-1600x875.gif" style="width: 100%;" /><p>Como você pode ver, temos alguns valores ausentes, <strong>ELASTIC_URL</strong> e <strong>ELASTIC_API_KEY,</strong> que precisam ser adicionados ao <em>.env</em> arquivo. Vamos fazer isso agora criando um projeto sem servidor Elasticsearch para servir como backend que alimentará a funcionalidade de pesquisa do nosso aplicativo de exemplo.</p><h2>Criar um projeto Elastic Serverless</h2><ol><li>Acesse <a href="http://cloud.elastic.co">cloud.elastic.co</a> e clique em <strong>Criar novo projeto sem servidor</strong></li><li>Clique em <strong>Avançar </strong>para a solução <strong>Elasticsearch</strong></li><li>Selecione <strong>Otimizado para Vetores</strong></li><li>Defina o <strong>Provedor de Nuvem</strong> como <strong>Azure</strong></li><li>Clique em <strong>Criar projeto sem servidor</strong></li><li>Clique em <strong>Introdução</strong> no menu de navegação principal e role para baixo para copiar os <strong>Detalhes da Conexão</strong></li><li>Clique no botão <strong>Copiar </strong>para copiar o <strong>Endpoint do Elasticsearch</strong> dos <strong>Detalhes da Conexão</strong></li><li>Atualizar <strong><em>.env</em></strong> arquivo para definir o <strong>ELASTIC_URL</strong> como o <strong>Elasticsearch Endpoint</strong>copiado</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/77a35e55446d396066b68cfd132d1543a07b81cc-1600x875.gif" style="width: 100%;" /><h2>Criar uma chave de API elástica</h2><ol><li>Abra a página <strong>Introdução</strong> ao Elasticsearch, clique em <strong>Novo</strong> na seção <strong>Adicionar uma chave de API</strong></li><li>Digite um <strong>nome</strong>de chave</li><li>Clique em<strong> Criar chave de API</strong></li><li>Clique no botão Copiar para copiar o valor da chave da API</li><li>De volta ao <strong>Codespaces,</strong> onde temos o <strong><em>.env</em> </strong>arquivo aberto para edição, cole o valor copiado para definir a<strong> ELASTIC_API_KEY</strong></li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/20168cba493d8e2c0d9ae7704eb0ae707df58e4c-1600x875.gif" style="width: 100%;" /><h2>Abra o Codespaces Notebook e instale as dependências da biblioteca</h2><p>No explorador de arquivos, selecione o arquivo <strong><em>VectorDBSetup.ipynb</em></strong> para abrir o Notebook. Após o carregamento do Notebook, localize a <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L40-L52">seção Notebook intitulada <strong>Instalar bibliotecas</strong></a><strong>. </strong>Clique no botão de reprodução da seção.</p><p>Se esta for a primeira vez que você executa um Notebook no GitHub Codespaces, você será solicitado a selecionar um Kernel do Codespaces e configurar o ambiente Python.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2272f70615dfb9dcbeb91f39b6dd5076213e24a5-1600x875.gif" style="width: 100%;" /><h2>Defina importações e carregue variáveis de ambiente usando o Codespaces Notebook</h2><p>Vá para a próxima seção no Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L58-L104">intitulada <strong>Definir importações e carregar variáveis de ambiente</strong></a>. Clique no botão de reprodução da seção.</p><p>Este código importa as bibliotecas Python usadas pelo Notebook e carrega as variáveis de ambiente do <em>.env </em>que você criou anteriormente.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aa9f9198ff452ac0c4ce33b00f253731dbee22c5-1600x875.gif" style="width: 100%;" /><h2>Crie um ponto de extremidade de inferência do Elastic ML usando o Codespaces Notebook</h2><p>Vá para a próxima <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L112-L157">seção do Notebook intitulada <strong>Criar ponto de extremidade de inferência de ML</strong></a>. Clique no botão de reprodução da seção.</p><p>Isso criará um novo ponto de extremidade de inferência de ML no projeto Elasticsearch que usaremos para gerar incorporações de texto a partir dos nossos dados. Incorporações de texto são representações vetoriais de texto que serão armazenadas no Elasticsearch para potencializar a pesquisa semântica.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/97c1afab3e64027ee5ae77f377d56ba406ae1765-1600x875.gif" style="width: 100%;" /><h2>Crie um índice Elasticsearch usando o Codespaces Notebook</h2><p>Vá para a próxima <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L165-L224">seção do Notebook intitulada <strong>Criar índice do Elasticsearch</strong></a>. Clique no botão de reprodução da seção.</p><p>Isso criará o índice do Elasticsearch que armazenará nossos dados de exemplo e quaisquer dados vetoriais associados gerados por meio do ponto de extremidade de inferência de ML.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23a66283ee41239e24fb8455c3cd95641982ca6b-1600x875.gif" style="width: 100%;" /><h2>Crie um modelo de pesquisa Elasticsearch usando o Codespaces Notebook</h2><p>Continue para a próxima <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L232-L384">seção do Notebook intitulada <strong>Modelo de Pesquisa</strong></a>. Clique no botão de reprodução da seção.</p><p>Isso cria um <a href="https://www.elastic.co/pt/docs/solutions/search/search-templates">modelo de pesquisa</a>, que é o que nosso aplicativo de exemplo usará como modelo preenchido com as palavras analisadas da consulta de pesquisa de um usuário. Isso nos permite configurar e controlar o quão específicos somos ao consultar dados no índice do Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4c001a3e4d1cca4cfb5c043fea92c7ccaf9cb64a-1600x875.gif" style="width: 100%;" /><h2>Ingerir dados no índice do Elasticsearch usando o Codespaces Notebook</h2><p>Vá para a próxima seção do Notebook <a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L392-L450">intitulada <strong>Ingerir dados de propriedade</strong></a>. Clique no botão de execução da seção.</p><p>A execução desta seção de código carregará em massa os dados de exemplo contidos no arquivo <em>properties.jsonl </em> . Após alguns minutos, você verá uma confirmação de que o processo foi concluído com sucesso. Você pode confirmar se o índice contém o registro esperado acessando a seção <strong>Gerenciamento de Índice</strong> no Elastic Cloud.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e9698c798541ccfc08143a939846597028e3c566-1600x875.gif" style="width: 100%;" /><h2>Crie appsetting.json para configurar o aplicativo C#</h2><p>Com nosso índice do Elasticsearch criado e preenchido com dados, agora estamos prontos para configurar o aplicativo de exemplo para funcionar com o Elastic e o Azure Cloud. O aplicativo de exemplo C# usa um arquivo chamado <em>appsettings.json</em> para armazenar e carregar suas informações de acesso, como chaves de API. Agora você criará o arquivo <em>appsettings.json</em> usando o editor no Codespaces.</p><p>1. Crie <em>appsettings.json</em> na pasta <strong>HomeFinderApp </strong></p><p>2. Cole o seguinte código no arquivo <em>appsettings.json</em></p><pre><code>{
 "ElasticSettings": {
   "Url": "",
   "ApiKey": "",
   "IndexName": "properties",
   "TemplateId": "properties-search-template"
 },
 "AzureOpenAISettings": {
   "Endpoint": "",
   "ApiKey": "",
   "DeploymentName": "gpt-4o"
 },
 "AzureMapsSettings": {
   "Url": "https://atlas.microsoft.com/geocode",
   "ApiKey": ""
 },
 "Logging": {
   "LogLevel": {
 	"Default": "Information",
 	"Microsoft.AspNetCore": "Warning"
   }
 },
 "AllowedHosts": "*"
}
</code></pre><p>3. Encontre os valores <strong>Url</strong> e <strong>ApiKey</strong> na seção <strong>ElasticSettings </strong> . Defina-os para os mesmos valores que você definiu no <em>.env</em> arquivo em uma etapa anterior.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00dad25bacdea2adcbd1e6eca7658867a49b0d8c-1600x875.gif" style="width: 100%;" /><h2>Criar serviço Azure OpenAI</h2><p>Nosso aplicativo de exemplo usará o Azure OpenAI para analisar a consulta do usuário do aplicativo e, em seguida, enviar uma solicitação ao Elasticsearch preenchendo o modelo de Pesquisa para tentar comunicar de forma flexível o que o usuário está pesquisando.</p><ol><li>Abra uma nova aba do navegador e acesse <a href="https://portal.azure.com/#blade/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/OpenAI">AI Foundry | Azure OpenAI</a> no portal do Azure. Clique +<strong>Criar</strong></li><li>No formulário de criação, escolha um <strong>Grupo de Recursos</strong>.</li><li>Digite um <strong>nome</strong></li><li>Escolha um <strong>nível de preço</strong></li><li>Clique <strong>em Avançar</strong></li><li>Na guia <strong>Rede</strong> , clique em<strong> Avançar</strong></li><li>Na aba <strong>Tags</strong> , clique em <strong>Avançar</strong></li><li>Na guia <strong>Revisar e enviar</strong> , clique em <strong>Criar</strong></li><li>Após a conclusão da criação, clique em <strong>Ir para o recurso</strong></li><li>Selecione <strong>Chaves e Ponto de Extremidade</strong> no menu de navegação à esquerda</li><li>Copie o <strong>Endpoint</strong> e cole-o no arquivo <em>appsettings.json</em> que você criou na aba do seu navegador que tem o editor Codespaces aberto.</li><li>Em seguida, volte para a aba do navegador que contém a <strong>chave do Azure OpenAI e a página do ponto de extremidade</strong>. Clique no botão copiar para <strong>Chave 1</strong> e cole o valor copiado no arquivo <em>appsettings.json</em> , de volta na aba do seu navegador que tem o editor Codespaces aberto.</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6d49197302d110410dca0a53b6ae90237cf2dfd6-1600x875.gif" style="width: 100%;" /><h2>Adicionar implantação do modelo gpt-4o ao serviço Azure Open AI</h2><p>Ótimo, agora temos um serviço Azure OpenAI em execução, mas ele ainda precisa de uma implantação de modelo para nos dar os recursos de LLM que nosso aplicativo de exemplo requer. Há uma infinidade de modelos para você escolher. Vamos implantar <em>o gpt-4o,</em> pois ele já está especificado no arquivo <em>appsettings.json</em> que você criou.</p><p></p><ol><li>Acesse o <a href="https://ai.azure.com/resource/playground">Azure AI Foundry</a> e clique em <strong>Criar uma implantação</strong></li><li>Pesquise por <em>gpt-4o</em> e selecione-o nos resultados</li><li>Clique em <strong>Confirmar</strong> para selecioná-lo</li><li>Clique em <strong>Implantar</strong> para implantar o modelo</li></ol><p>Depois de implantar com sucesso o modelo <em>gpt-4o</em> , você pode selecionar <strong>Implantações</strong> no menu de navegação à esquerda e confirmar se a implantação <strong><em>do gpt-4o</em></strong> está listada com o <strong>Estado</strong> <strong>Succeeded</strong>.<br/></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1b817ab67c05634e9c72777593b4d1a2c6c28191-1600x875.gif" style="width: 100%;" /><h2>Criar conta do Azure Maps</h2><p>Queremos que os usuários do nosso aplicativo de exemplo possam pesquisar propriedades imobiliárias em áreas específicas, mas sem precisar ser muito específicos. Se alguém quiser procurar uma propriedade perto do mercado local de produtores rurais, o Azure Maps é um serviço que o OpenAI LLM pode usar para obter coordenadas de latitude e longitude do mercado. As coordenadas podem então ser incluídas nas solicitações baseadas em modelo de pesquisa enviadas ao Elasticsearch para consultas de usuários que incluem localizações específicas e considerações de distância geográfica.</p><ol><li>Clique em <strong>Criar</strong> nas <a href="https://portal.azure.com/#browse/Microsoft.Maps%2Faccounts">contas do Azure Maps</a></li><li>Selecione um <strong>grupo de recursos</strong></li><li>Digite um <strong>nome</strong></li><li>Concordo com a declaração de licença e privacidade</li><li>Clique em <strong>Revisar e criar</strong></li><li>Clique em <strong>Criar</strong></li><li>Após a criação da conta, clique em <strong>Ir para o recurso</strong></li><li>Clique em <strong>Autenticação</strong> no menu de navegação à esquerda</li><li>Copie o valor <strong>da Chave Primária</strong> e cole-o para ser o valor da <strong>ApiKey</strong> na seção <strong>AzureMapsSettings</strong> do arquivo <em>appsettings.json</em> , de volta na guia do navegador que contém o editor Codespaces</li></ol><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/90fadd48e366682e2bad91e32988f93c6354e126-1600x875.gif" style="width: 100%;" /><h2>Experimente o aplicativo de exemplo</h2><p>Agora vem a parte divertida. É hora de executar o aplicativo de exemplo. Temos todos os detalhes de configuração prontos, juntamente com os recursos do Elastic Cloud e do Azure Cloud necessários para executar o aplicativo.</p><p>1. Abra uma janela do Terminal no editor Codespaces.</p><p>2. Use o seguinte comando para alterar o diretório ativo para ser a pasta do aplicativo de exemplo.<br/></p><pre><code>cd HomeFinderApp</code></pre><p>3. Use o seguinte comando <em>dotnet</em> para executar o aplicativo.</p><pre><code>dotnet run</code></pre><p>4. Clique no botão <strong>Abrir no navegador </strong>quando ele aparecer.</p><p>5. Teste a pesquisa padrão e depois experimente algumas de suas próprias pesquisas personalizadas. Se quiser ver mais detalhes sobre o que é executado no backend para gerar os resultados da pesquisa, clique no link <strong>Mostrar </strong>ao lado de <strong>Invocações de ferramentas.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/821fe7b9446de5ed646d938cc9484a7ddad21030-1600x875.gif" style="width: 100%;" /><p><strong>Bônus: </strong>Se você realmente quiser testar o GPT-4o, tente a seguinte pesquisa: <em>Estou procurando um imóvel perto da Disney World, Flórida, com mais de 30 quartos, mais de 20 banheiros, piscina, garagem e perto da praia por menos de 200 mil. </em>Esta consulta retornará resultados após várias invocações da ferramenta de pesquisa.</p><h2>Elastic é a sua solução para IA de pesquisa</h2><p>O aplicativo em execução é um exemplo de pesquisa guiada do Gen AI LLM usando o Elasticsearch por meio de modelos de pesquisa como fonte de dados de base. Sinta-se à vontade para experimentar e personalizar o aplicativo de exemplo para criar uma experiência de pesquisa precisa e flexível, para ajudar seus usuários a encontrar o que procuram.</p><p>Obrigado pela leitura. Experimente <a href="https://cloud.elastic.co/registration">o Elastic Cloud</a> .</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[O estado atual do MCP (Model Context Protocol)]]></title>
            <description><![CDATA[Saiba mais sobre o MCP, atualizações de projetos, recursos, desafios de segurança, casos de uso emergentes e como mexer no servidor Elasticsearch MCP da Elastic.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/mcp-current-state</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/mcp-current-state</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[JD Armada]]></dc:creator>
            <pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02b8c8672ffa129e0ed91a92d6cab612a01d27f2-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Recentemente, participei da <a href="https://mcpdevsummit.ai/">Cúpula de Desenvolvedores do MCP</a> em São Francisco e ficou claro que o Protocolo de Contexto de Modelo (MCP) está se tornando rapidamente um elemento fundamental para agentes de IA e aplicações de IA ricas em contexto. Na Elastic, estamos caminhando nessa direção, expondo servidores MCP diretamente do <a href="https://www.elastic.co/pt/elasticsearch/agent-builder">Agent Builder</a>, tornando o Elasticsearch um provedor de contexto e ferramentas de primeira classe para qualquer agente compatível com MCP. Neste post, abordarei as principais atualizações do evento, os casos de uso emergentes, o que está por vir para o MCP e como você pode usar o Agent Builder para disponibilizar o Elasticsearch aos agentes por meio do MCP.</p><h2>O que é o Protocolo de Contexto do Modelo (MCP)?</h2><p>Para quem não conhece, <a href="https://modelcontextprotocol.io/introduction">o Model Context Protocol</a> é um padrão aberto que oferece uma maneira estruturada e bidirecional de conectar modelos de IA a várias fontes de dados e ferramentas, permitindo que eles gerem respostas mais relevantes e informadas. É comumente chamada de “<a href="https://modelcontextprotocol.io/introduction">porta USB-C para aplicativos de IA</a>”.</p><p>Aqui está um diagrama arquitetônico que destaca sua natureza bidirecional:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5eba1e59514eb58a5220bb92bb49e6328ee83cd7-674x466.png" style="width: 100%;" /><p>Esta é uma mudança significativa para os profissionais de IA, pois um dos principais desafios para dimensionar aplicativos de IA é ter que criar integrações personalizadas para cada nova fonte de dados. O MCP oferece uma arquitetura sustentável e reutilizável para gerenciar e fornecer contexto aos modelos. É independente de modelo, independente de servidor e totalmente de código aberto.</p><p>O MCP é a mais recente iteração de uma linhagem de especificações de API que buscam padronizar a integração entre aplicativos. No passado, tínhamos OpenAPI para serviços RESTful, GraphQL para consulta de dados e gRPC para comunicação de microsserviços. O MCP não apenas compartilha o rigor estruturado dessas especificações mais antigas, mas também as traz para um ambiente de IA generativo, facilitando a conexão de agentes em diferentes sistemas sem conectores personalizados. De muitas maneiras, o MCP pretende fazer pelos agentes de IA o que o HTTP fez pela web. Assim como o HTTP padronizou a comunicação entre navegadores e sites, o MCP busca padronizar como os agentes de IA interagem com o mundo de dados ao seu redor.</p><h2>MCP vs. outros protocolos de agentes</h2><p>O cenário de protocolos de agentes está se expandindo rapidamente, com mais de uma dúzia de padrões emergentes competindo para definir como os agentes interagem. <a href="https://x.com/seldo">Laurie Voss,</a> do LlamaIndex, descreve como a maioria pode ser categorizada em dois tipos: protocolos interagentes, que se concentram em agentes conversando entre si, e protocolos orientados a contexto, como o MCP, que se concentram em fornecer contexto estruturado aos LLMs.</p><p>Outros protocolos populares, como <a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">o A2A</a> (Agent to Agent) do Google, <a href="https://agentcommunicationprotocol.dev/introduction/welcome">o ACP</a> (Agent Communication Protocol) da Cisco e da IBM e <a href="https://agoraprotocol.org/">o Agora</a>, visam permitir negociações entre agentes, construção de coalizões e até mesmo sistemas de identidade descentralizados. O MCP adota uma abordagem um pouco mais pragmática, pois se concentra em como os agentes acessam ferramentas e dados e não necessariamente como eles se comunicam entre si (embora o MCP também possa permitir isso no futuro de diferentes maneiras).</p><p>Atualmente, o que diferencia o MCP é sua tração e impulso. Assim como o React nos primeiros dias dos frameworks de front-end, o MCP começou com um problema de nicho e agora é um dos protocolos de agente mais adotados e extensíveis na prática.</p><h2>Recapitulação da cúpula: Prioridades em evolução para o MCP</h2><p>A cúpula contou com palestrantes de colaboradores da Anthropic, Okta, OpenAI, AWS, GitHub e muitos outros. As palestras abrangeram desde melhorias no protocolo principal até implementações no mundo real e delinearam prioridades imediatas e de longo prazo. Essas palestras refletiram uma mudança da experimentação inicial e da simples chamada de ferramentas para a construção de sistemas de IA confiáveis, escaláveis e modulares usando o MCP como base.</p><p>Vários palestrantes sugeriram um futuro em que o MCP será mais do que apenas um protocolo de encanamento; ele poderá se tornar a base de uma web nativa de IA. Assim como o JavaScript permitiu que os usuários clicassem e interagissem com páginas da web, o MCP poderia permitir que agentes realizassem as mesmas ações em nosso nome. Por exemplo, no comércio eletrônico, em vez de os usuários navegarem manualmente até um site para comprar, eles poderiam simplesmente dizer a um agente para fazer login, encontrar um produto específico, adicioná-lo ao carrinho e finalizar a compra.</p><p>Isso não é apenas pura especulação e exagero; o PayPal apresentou seu novo kit de ferramentas para agentes e servidor MCP na cúpula, o que possibilita exatamente essa experiência de comércio com agentes. Com o MCP fornecendo acesso seguro e confiável a ferramentas e fontes de dados, os agentes não apenas lerão a web, mas também poderão agir com base nela. Hoje, o MCP já é um padrão poderoso e com muita força e, no futuro, pode se tornar o padrão de interações de usuários aprimoradas por IA na web.</p><h2>Atualizações do projeto MCP: transporte, elicitação e ferramentas estruturadas</h2><p><a href="https://x.com/JeromeSwannack">Jerome Swannack</a>, um dos principais colaboradores do MCP, compartilhou algumas atualizações da especificação do protocolo dos últimos 6 meses. Os principais objetivos dessas mudanças são:</p><ol><li>Para habilitar o MCP remoto com a adição do Streamable HTTP</li><li>Para permitir modelos de interação de agentes mais ricos com a adição de Elicitação e Esquemas de Saída de Ferramentas</li></ol><p>Como o MCP é de código aberto, mudanças como o Streamable HTTP já estão disponíveis para os desenvolvedores implementarem. Os esquemas de elicitação e saída de ferramentas ainda não foram lançados; eles estão em fase de rascunho e podem evoluir.</p><p><strong>HTTP transmissível </strong>(<a href="https://modelcontextprotocol.io/specification/2025-03-26/basic/transports">lançado em 26/03/2025</a>)<strong>:</strong> Uma atualização técnica impactante foi a introdução do HTTP transmissível como um novo mecanismo de transporte. Isso substitui eventos enviados pelo servidor (SSE) por um modelo bidirecional mais escalável que oferece suporte à codificação de transferência em blocos e à entrega progressiva de mensagens em uma única conexão HTTP. Isso permite que você implante servidores MCP em infraestrutura de nuvem como AWS Lambda e ofereça suporte a restrições de rede corporativa sem conexões de longa duração ou necessidade de sondagem.</p><p><strong>Elicitação </strong>(<a href="https://modelcontextprotocol.io/specification/2025-06-18/client/elicitation">lançado em 18/06/2025</a>)<strong>:</strong> a elicitação permite que os servidores definam um esquema de como eles querem que o contexto seja estruturado a partir de um cliente. Basicamente, o servidor pode descrever o que precisa e o tipo de entrada que espera. Isso tem algumas implicações: para os construtores de servidores, eles podem criar interações de agentes mais complexas. Para construtores de clientes, eles podem implementar interfaces de usuário dinâmicas que se adaptam a esses esquemas. No entanto, a elicitação não deve ser usada para extrair informações confidenciais ou pessoalmente identificáveis dos usuários. Os desenvolvedores devem seguir <a href="https://modelcontextprotocol.io/specification/draft/client/elicitation#security-considerations">as melhores práticas</a> para garantir que os prompts de elicitação permaneçam seguros e apropriados, especialmente à medida que o MCP amadurece. Isso está ligado a preocupações de segurança mais amplas que discutiremos mais adiante neste post.</p><p><strong>Esquemas de saída de ferramentas </strong>(<a href="https://modelcontextprotocol.io/specification/draft/server/tools#output-schema">lançados em 18/06/2025</a>)<strong>: </strong>este conceito permite que o cliente e o LLM conheçam as formas de saída da ferramenta com antecedência. Os esquemas de saída da ferramenta permitem que os desenvolvedores descrevam o que se espera que uma ferramenta retorne. Esses esquemas abordam uma das principais limitações da chamada direta de ferramentas, que é o uso ineficiente da janela de contexto. A janela de contexto é considerada um dos recursos mais importantes ao trabalhar com LLMs e, quando você chama uma ferramenta diretamente, ela retorna conteúdo bruto que é totalmente inserido no contexto do LLM. Os esquemas de saída da ferramenta podem ajudar você a fazer melhor uso dos seus tokens e da janela de contexto, permitindo que o servidor MCP forneça dados estruturados. Aqui estão algumas <a href="https://modelcontextprotocol.io/specification/draft/server/tools#security-considerations">práticas recomendadas</a> sobre ferramentas em geral.</p><p>Juntas, essas novas atualizações e adições futuras ajudarão o MCP a se tornar um protocolo de agente mais modular, tipado e pronto para produção.</p><h2>Recursos de energia subutilizados: amostragem e raízes</h2><p>Embora não seja novidade na especificação MCP, tanto a amostragem quanto as raízes foram destacadas durante a palestra. Essas duas primitivas são atualmente negligenciadas e pouco exploradas, mas podem contribuir significativamente para interações mais ricas e seguras entre agentes.</p><p><strong>Amostragem - Os servidores podem solicitar conclusões do cliente: </strong><a href="https://modelcontextprotocol.io/docs/concepts/sampling">A amostragem</a> permite que os servidores MCP solicitem conclusões do LLM do lado do cliente. Isso aumenta a natureza bidirecional do protocolo, onde o servidor não está apenas respondendo às solicitações; ele pode solicitar e pedir ao modelo do cliente para gerar uma resposta. Isso permite que o cliente mantenha controle total sobre o custo, a segurança e qual modelo o servidor MCP usa. Portanto, no caso de usar um servidor MCP externo com um modelo pré-configurado, você não precisará fornecer suas próprias chaves de API ou configurar sua própria assinatura para esse modelo, pois o servidor pode simplesmente solicitar o modelo já conectado ao cliente. Isso permite comportamentos de agentes mais complexos e interativos.</p><p><strong>Raízes - Acesso com escopo aos recursos: </strong><a href="https://modelcontextprotocol.io/docs/concepts/roots">As raízes</a> foram projetadas para fornecer uma maneira para os clientes informarem os servidores sobre recursos e espaços de trabalho relevantes nos quais se concentrar. Isso é útil para definir o escopo no qual os servidores operam. É importante observar que as raízes são “<a href="https://modelcontextprotocol.io/docs/concepts/roots#how-roots-work">informativas e não estritamente obrigatórias</a>”, o que significa que elas não definem direitos ou permissões para servidores ou agentes MCP. Em outras palavras, você não pode confiar apenas nas raízes para impedir que um servidor ou agente execute determinadas ferramentas ou realize ações de gravação. Com raízes, as permissões ainda devem ser manipuladas no lado do cliente com mecanismos para aprovação do usuário. Além disso, os desenvolvedores ainda devem estar atentos ao uso de servidores projetados para respeitar os limites definidos pelas raízes e usar <a href="https://modelcontextprotocol.io/docs/concepts/roots#best-practices">as melhores práticas</a>.</p><h2>Autenticação para agentes: OAuth 2.1 e metadados protegidos</h2><p>Esta seção se concentra no OAuth 2.1, que é a iteração mais recente do OAuth 2.0 que remove fluxos inseguros e consolida as melhores práticas.</p><p>O suporte ao OAuth era um tópico muito aguardado, especialmente porque a segurança e a escalabilidade são vistas como os principais obstáculos que impedem o MCP de se tornar o padrão para conectar agentes a ferramentas. <a href="https://x.com/aaronpk">Aaron Parecki</a> (editor do OAuth 2.1 e especialista em padrões de identidade na Okta) discutiu como o MCP pode adotar um fluxo OAuth limpo e escalável que alivia a maior parte da complexidade dos desenvolvedores de servidores. A especificação oficial de autorização OAuth 2.1 foi publicada recentemente na última revisão do protocolo em <a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization">18/06/2025</a>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2ea159116fe5e03ff800f077adf16d6ca9f1c1d1-1594x1280.png" style="width: 100%;" /><p>Nesta implementação, as responsabilidades do OAuth podem ser divididas entre o cliente MCP e o servidor. A maior parte do fluxo de autenticação é iniciada e gerenciada pelo cliente MCP, envolvendo apenas o servidor no final para receber e verificar o token seguro. Essa divisão ajuda a resolver um problema crítico de dimensionamento de como autenticar em muitas ferramentas sem exigir que os desenvolvedores configurem cada conexão e garante que os desenvolvedores do servidor MCP não precisem se tornar especialistas em OAuth.</p><p>Dois destaques principais da palestra:</p><ol><li><a href="https://datatracker.ietf.org/doc/rfc9728/"><strong>Metadados de recursos protegidos</strong></a>: os servidores MCP podem publicar um arquivo JSON descrevendo sua finalidade, pontos de extremidade e métodos de autenticação. Isso permite que os clientes iniciem fluxos OAuth apenas com a URL do servidor, simplificando o processo de conexão. Saiba mais: <a href="https://aaronparecki.com/2025/04/03/15/oauth-for-model-context-protocol">Vamos corrigir o OAuth no MCP</a></li><li><a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13"><strong>Suporte para IDPs e SSO</strong></a>: as empresas podem integrar provedores de identidade para gerenciar o acesso centralmente. Isto é uma vitória tanto para a experiência do usuário quanto para a segurança. Os usuários não precisariam clicar em 10 telas de consentimento diferentes e as equipes de segurança poderiam ter visibilidade de cada conexão.</li></ol><p>Ao enviar a lógica do OAuth para o cliente e confiar nos metadados dos servidores, o ecossistema MCP evita um grande gargalo. Isso alinha o MCP mais de perto com a forma como as APIs modernas são protegidas nos ambientes de produção atuais.</p><p>Leitura adicional: <a href="https://aaronparecki.com/oauth-2-simplified/">OAuth 2 simplificado</a>.</p><h2>Desafios de segurança em um ecossistema componível</h2><p>Novos desenvolvimentos também trazem novas superfícies de ataque. Arjun Sambamoorthy, da Cisco, lista diversas ameaças importantes no cenário do MCP, incluindo:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ameaça</td><td style="border: 1px solid #ccc; padding: 4px;">Descrição</td><td style="border: 1px solid #ccc; padding: 4px;">Remediação e melhores práticas</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Injeção imediata e envenenamento por ferramentas</td><td style="border: 1px solid #ccc; padding: 4px;">Uma maneira de injetar um prompt malicioso dentro do contexto do sistema LLM ou da descrição da ferramenta, fazendo com que o LLM execute ações não intencionais, como ler arquivos ou vazar dados.</td><td style="border: 1px solid #ccc; padding: 4px;">Use ferramentas como o MCP Scan para realizar verificações nos metadados das ferramentas. Valide descrições e parâmetros antes de incluí-los nos prompts. Por fim, considere implementar aprovações de usuários para ferramentas de alto risco. Para mais detalhes, consulte o guia de injeção rápida do OWASP na lista de leitura adicional abaixo da tabela.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ataques de amostragem</td><td style="border: 1px solid #ccc; padding: 4px;">No contexto do MCP, a amostragem abre a porta para o servidor MCP realizar ataques de injeção rápida no LLM.</td><td style="border: 1px solid #ccc; padding: 4px;">Desative a amostragem para servidores não confiáveis e considere adicionar aprovações humanas para solicitações de amostragem.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Servidores MCP maliciosos</td><td style="border: 1px solid #ccc; padding: 4px;">Nas coleções atuais de servidores MCP, é difícil verificar cada um deles para garantir a segurança. Servidores invasores podem coletar e expor silenciosamente seus dados a agentes maliciosos.</td><td style="border: 1px solid #ccc; padding: 4px;">Conecte-se somente a servidores MCP de registros confiáveis ou listas internas. Execute servidores de terceiros em contêineres com sandbox.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Ferramentas de instalação de MCP maliciosas</td><td style="border: 1px solid #ccc; padding: 4px;">Instaladores de linha de comando e scripts são convenientes para implementar rapidamente servidores ou ferramentas MCP, mas você pode acabar instalando código comprometido e não verificado.</td><td style="border: 1px solid #ccc; padding: 4px;">Instale em ambientes sandbox e valide assinaturas de pacotes. Nunca atualize automaticamente a partir de fontes não verificadas.</td>
                    </tr>
              </tbody>
            </table>
          <p>Para combater ainda mais isso, Arjun sugere um registro MCP confiável para lidar com todas as verificações (um tópico que estava em destaque — para mais detalhes, veja os dois principais itens na lista de leitura abaixo), bem como usar esta <a href="https://github.com/slowmist/MCP-Security-Checklist">lista de verificação de segurança</a>.</p><p>Leitura adicional:</p><ul><li><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices">Melhores práticas oficiais de segurança do MCP</a></li><li><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">Top 10 de inscrições para o OWASP LLM</a></li><li><a href="https://hiddenlayer.com/innovation-hub/">Pesquisa de ameaças HiddenLayer</a></li><li><a href="https://github.com/invariantlabs-ai/mcp-scan">Varredura MCP</a></li><li><a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/">Guia de injeção rápida OWASP</a></li></ul><h2>O que vem a seguir: Registros, governança e ecossistema</h2><p>Um registro centralizado do MCP está em desenvolvimento e foi um dos tópicos mais consistentemente discutidos na cúpula. O ecossistema de servidores atual sofre de fragmentação, baixa confiança e capacidade de descoberta. É difícil para os desenvolvedores encontrar servidores MCP, verificar o que eles fazem e instalá-los com segurança, especialmente em um ecossistema descentralizado onde os metadados podem estar incompletos ou falsificados.</p><p>Um registro centralizado aborda esses pontos problemáticos diretamente, agindo como uma fonte confiável de verdade, melhorando a capacidade de descoberta, garantindo a integridade dos metadados do servidor e reduzindo o risco de instalação de ferramentas maliciosas.</p><p>Os objetivos do registro MCP são:</p><ul><li>Oferecendo uma única fonte de verdade para metadados do servidor (o que um servidor faz, como autenticar, instalá-lo e chamá-lo)</li><li>Eliminar registros de terceiros incompletos e fragmentação para que, quando um servidor quiser ser registrado, ele não precise atualizar todos os outros registros na Internet.</li><li>Fornecendo um fluxo de registro de servidor que inclui uma ferramenta CLI e um arquivo server.json que contém os metadados mencionados anteriormente.</li></ul><p>A esperança mais ampla é que um registro confiável ajude a dimensionar o ecossistema com segurança, permitindo que os desenvolvedores criem e compartilhem novas ferramentas com confiança.</p><p>Governança foi outra questão prioritária para a Anthropic. Eles deixaram claro que o MCP deve permanecer aberto e orientado pela comunidade, mas dimensionar esse modelo de governança ainda é um trabalho em andamento. Atualmente, eles estão buscando ajuda nessa área e pedem que qualquer pessoa que tenha experiência com governança em protocolos de código aberto entre em contato. Isso nos leva ao outro tópico que eu queria mencionar. Durante o evento, os palestrantes enfatizaram que o ecossistema só pode crescer com contribuições dos desenvolvedores internos. É preciso haver um esforço concentrado para tornar o MCP o novo padrão da web e se destacar dos outros protocolos de agentes populares.</p><h2>MCP no mundo real: estudos de caso e demonstrações</h2><p>Várias organizações compartilharam como o MCP já está sendo usado em aplicações práticas:</p><ul><li><strong>PayPal - Servidor MCP para comércio de agentes: </strong>o PayPal apresentou seu novo <a href="https://github.com/paypal/agent-toolkit/">kit de ferramentas de agente</a> e servidor MCP, que pode mudar fundamentalmente a experiência de compra do usuário. Em vez de vasculhar as redes sociais para encontrar itens, comparar preços e finalizar a compra, os usuários podem conversar com um agente que se conecta ao servidor MCP do PayPal para lidar com todas essas ações.<br/></li><li><strong>EpicAI.pro - Jarvis:</strong> Os desenvolvimentos no MCP nos deixam cada vez mais perto de ter um assistente real do tipo Jarvis. Para quem não conhece os filmes do Homem de Ferro, Jarvis é um assistente de IA que usa linguagem natural, responde a entradas multimodais, tem latência zero ao responder, é proativo em antecipar as necessidades do usuário, gerencia integrações automaticamente e pode alternar o contexto entre dispositivos e locais. Se imaginarmos Jarvis como um assistente robótico físico, o MCP dá a Jarvis “mãos” ou a capacidade de lidar com tarefas complexas.<br/></li><li><strong>Postman - <a href="https://www.postman.com/explore/mcp-generator">Gerador de servidor MCP</a>: </strong>fornece uma experiência de carrinho de compras para solicitações de API, onde você pode escolher diferentes solicitações de API, colocá-las em uma cesta e baixar a cesta inteira como um servidor MCP.<br/></li><li><strong>Bloomberg - </strong>A Bloomberg resolveu um gargalo importante no desenvolvimento empresarial de GenAI. Com quase 10.000 engenheiros, eles precisavam de uma maneira padronizada de integrar ferramentas e agentes entre as equipes. Com o MCP, eles transformaram suas ferramentas internas em componentes modulares e remotos que os agentes podem facilmente chamar em uma interface unificada. Isso permitiu que seus engenheiros contribuíssem com ferramentas em toda a organização, enquanto as equipes de IA se concentravam na criação de agentes em vez de integrações personalizadas. A Bloomberg agora oferece suporte a fluxos de trabalho de agentes escaláveis e seguros que desbloqueiam total interoperabilidade com o ecossistema MCP. A Bloomberg não divulgou nenhum recurso público, mas foi isso que eles apresentaram publicamente na cúpula.<br/></li><li><strong>Block - </strong>O Block usa o MCP para impulsionar <a href="https://github.com/block/goose?tab=readme-ov-file">o Goose</a>, um agente de IA interno que permite aos funcionários automatizar tarefas de engenharia, vendas, marketing e muito mais. Eles criaram mais de 60 servidores MCP para ferramentas como Git, Snowflake, Jira e Google Workspace para permitir interação em linguagem natural com os sistemas que eles usam todos os dias. Os funcionários da Block agora usam o Goose para consultar dados, detectar fraudes, gerenciar incidentes, navegar em processos internos e muito mais, tudo isso sem precisar escrever código. O MCP ajudou a Block a escalar a adoção de IA em muitas funções de trabalho em apenas 2 meses.<br/></li><li><strong>AWS - <a href="https://github.com/awslabs/mcp">Servidores MCP da AWS</a>: </strong>a AWS apresentou um divertido servidor MCP com tema de Dungeons and Dragons que simula o lançamento de dados, rastreia lançamentos anteriores e retorna resultados usando Streamable HTTP. Este exemplo simples destacou como é fácil construir e implantar servidores MCP usando ferramentas e infraestrutura da AWS, como Lambda e Fargate. Eles também introduziram <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">o Strands SDK</a>, um kit de ferramentas de código aberto para criar agentes multimodais que interagem com servidores MCP.</li></ul><h2>Suporte a MCP no Elastic Agent Builder</h2><p>Você pode começar a experimentar o MCP hoje mesmo usando <a href="https://www.elastic.co/pt/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">o Elastic Agent Builder,</a> que é a maneira mais fácil de criar agentes diretamente sobre seus dados. O Agent Builder permite expor ferramentas baseadas em Elasticsearch para agentes compatíveis com MCP e já vem com algumas ferramentas integradas poderosas, incluindo:</p><ul><li><code>platform.core.search</code> - Executa pesquisas usando a DSL de consulta completa do Elasticsearch</li><li><code>platform.core.list_indices</code> - Lista todos os índices disponíveis no Elasticsearch (ajuda os agentes a descobrir quais dados existem)</li><li><code>platform.core.get_index_mapping</code> - Recupera mapeamentos de campos para um índice específico (ajuda os agentes a entenderem o formato e os tipos dos seus dados)</li><li><code>platform.core.get_document_by_id</code> - Busca um documento específico por ID (para uma recuperação precisa)</li></ul><p>Somente com essas ferramentas, você pode equipar seu agente com pesquisa e relevância de nível empresarial, o que é fundamental para a criação de agentes de IA confiáveis.</p><p>O que torna o Agent Builder ainda mais poderoso é a capacidade de definir e expor suas próprias ferramentas personalizadas, adaptadas às necessidades do seu aplicativo. Isso é especialmente útil para fluxos de trabalho repetitivos ou com critérios predefinidos, nos quais você deseja que o agente execute um tipo específico de pesquisa em um índice específico, sem precisar redescobrir essa lógica a cada vez. Em vez de gastar tokens em planejamento e raciocínio para chegar à mesma conclusão, você pode codificar essa intenção diretamente em uma ferramenta, tornando seus agentes mais rápidos, confiáveis e econômicos.</p><p>Na interface do usuário do Agent Builder, aqui está um exemplo de definição de ferramenta personalizada que usa ES|QL:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1f6405a374b707e8e6fa36b9e21db5f3c7cd127-1376x864.png" style="width: 100%;" /><p>Depois de definir suas ferramentas personalizadas, você pode expô-las (além das ferramentas nativas integradas) usando o MCP clicando no menu suspenso para <code>Manage MCP</code> e copiando o URL do servidor MCP.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9f39588525ca2643475de557ea54a6bcf5c150f6-1282x616.png" style="width: 100%;" /><p>Agora você pode importar este endpoint MCP para qualquer cliente que utilize MCP, conectando-o ao Agent Builder e dando-lhe acesso a todas as ferramentas disponíveis. Para obter mais informações, leia esta introdução ao <a href="https://www.elastic.co/pt/search-labs/blog/elastic-ai-agent-builder-context-engineering-introduction">Agent Builder</a>.</p><h2>Conclusão</h2><p>O MCP Dev Summit deixou claro que o MCP está moldando a maneira como esses agentes de IA interagem entre si e com o mundo de dados ao seu redor. Não importa se você está conectando um agente a dados corporativos ou projetando agentes totalmente autônomos, o MCP oferece uma maneira padronizada e combinável de integração que está rapidamente se tornando útil em escala. De protocolos de transporte e padrões de segurança a registros e governança, o ecossistema MCP está amadurecendo rapidamente. O MCP continuará aberto e orientado pela comunidade, para que os desenvolvedores de hoje tenham a chance de moldar sua evolução.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Busca híbrida revisitada: apresentando o recuperador linear no Elasticsearch!]]></title>
            <description><![CDATA[Descubra como o recuperador linear aprimora a busca híbrida, aproveitando pontuações ponderadas e normalização MinMax para classificações mais precisas e consistentes, e aprenda a usá-lo.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/linear-retriever-hybrid-search</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/linear-retriever-hybrid-search</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 28 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7a07e27e3095463ff93b4cb7f8a0cf3b8e44eab0-1777x1000.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Em nossa postagem <a href="https://www.elastic.co/pt/search-labs/blog/elasticsearch-retrievers-ga-8.16.0">anterior,</a> apresentamos a estrutura de recuperadores redesenhada do zero, que permite a criação de pipelines de classificação complexos. Também exploramos como o recuperador Reciprocal Rank Fusion (RRF) permite a pesquisa híbrida ao mesclar resultados de diferentes consultas. Embora o RRF seja fácil de implementar, ele tem uma limitação notável: ele se concentra apenas em classificações relativas, ignorando pontuações reais. Isso torna o ajuste fino e a otimização um desafio.</p><h2>Conheça o retriever linear!</h2><p>Nesta postagem, apresentamos o <a href="https://www.elastic.co/pt/docs/solutions/search/retrievers-overview#retrievers-overview-types">recuperador</a> <a href="https://www.elastic.co/pt/docs/solutions/search/retrievers-overview#retrievers-overview-types"><code>linear</code></a> , nossa mais recente adição para oferecer suporte à pesquisa híbrida! Ao contrário de <code>rrf</code>, o recuperador <code>linear</code> calcula uma soma ponderada em todas as consultas que correspondem a um documento. Essa abordagem preserva a importância relativa de cada documento dentro de um conjunto de resultados, ao mesmo tempo que permite controle preciso sobre a influência de cada consulta na pontuação final. Como resultado, ele fornece uma maneira mais intuitiva e flexível de ajustar a pesquisa híbrida.</p><p>Definindo um recuperador linear onde a pontuação final será calculada como:</p><div class="katex">score = 5 * knn + 1.5 * bm25</div><p>É tão simples quanto:</p><pre><code>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5
               },


           ]
        }
     }
}</code></pre><p>Percebeu como é simples e intuitivo? (e muito parecido com <code>rrf</code>!) Essa configuração permite que você controle precisamente quanto cada tipo de consulta contribui para a classificação final, ao contrário de <code>rrf</code>, que depende apenas de classificações relativas.</p><p>Uma ressalva permanece: as pontuações <code>knn</code> podem ser estritamente limitadas, dependendo da métrica de similaridade usada. Por exemplo, com similaridade de cosseno ou produto escalar de vetores normalizados por unidade, as pontuações sempre estarão dentro do intervalo <code>[0, 1]</code> . Em contraste, as pontuações <code>bm25</code> são menos previsíveis e não têm limites claramente definidos.</p><h2>Escalando as pontuações: kNN vs BM25</h2><p>Um desafio da busca híbrida é que diferentes recuperadores produzem pontuações em escalas diferentes. Considere, por exemplo, o seguinte cenário:</p><p>Pontuações da consulta A:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">100</td><td style="border: 1px solid #ccc; padding: 4px;">1,5</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,5</td>
                    </tr>
              </tbody>
            </table>
          <p>Pontuações da consulta B:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">0,63</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,3</td><td style="border: 1px solid #ccc; padding: 4px;">0,4</td>
                    </tr>
              </tbody>
            </table>
          <p>Você pode ver a disparidade acima: as pontuações <code>kNN</code> variam entre 0 e 1, enquanto as pontuações <code>bm25</code> podem variar muito. Essa diferença dificulta a definição de pesos estáticos ideais para combinar os resultados.</p><h2>Normalização para o resgate: o normalizador MinMax</h2><p>Para resolver isso, introduzimos um normalizador <code>minmax</code> opcional que dimensiona as pontuações, independentemente para cada consulta, para o intervalo <code>[0, 1]</code> usando a seguinte fórmula:</p><div class="katex">normalized_score = (score - min) / (max - min)</div><p>Isso preserva a importância relativa de cada documento dentro do conjunto de resultados de uma consulta, facilitando a combinação de pontuações de diferentes recuperadores. Com a normalização, as pontuações se tornam:</p><p>Pontuações da consulta A:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1,00</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,005</td><td style="border: 1px solid #ccc; padding: 4px;">0,000</td>
                    </tr>
              </tbody>
            </table>
          <p>Pontuações da consulta B:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1,00</td><td style="border: 1px solid #ccc; padding: 4px;">0,000</td><td style="border: 1px solid #ccc; padding: 4px;">0,465</td><td style="border: 1px solid #ccc; padding: 4px;">0,645</td>
                    </tr>
              </tbody>
            </table>
          <p>Todas as pontuações agora estão no intervalo <code>[0, 1]</code> e otimizar a soma ponderada é muito mais simples, pois agora capturamos a importância (em relação à consulta) de um resultado em vez de sua pontuação absoluta e mantemos a consistência entre as consultas.</p><h2>Exemplo de recuperador linear </h2><p>Vamos ver um exemplo agora para mostrar a aparência do exemplo acima e como o recuperador <code>linear</code> aborda algumas das deficiências do <code>rrf</code>. O RRF depende somente de classificações relativas e não considera diferenças reais de pontuação. Por exemplo, dadas estas pontuações:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">100</td><td style="border: 1px solid #ccc; padding: 4px;">1,5</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,5</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">pontuação rrf</td><td style="border: 1px solid #ccc; padding: 4px;">0,03226</td><td style="border: 1px solid #ccc; padding: 4px;">0,03252</td><td style="border: 1px solid #ccc; padding: 4px;">0,03200</td><td style="border: 1px solid #ccc; padding: 4px;">0,03125</td>
                    </tr>
              </tbody>
            </table>
          <p>rrf classificaria os documentos como:</p><div class="katex">doc2 > doc1 > doc3 > doc4</div><p>No entanto, doc1 tem uma pontuação <code>bm25</code> significativamente maior que as outras, o que <code>rrf</code> não consegue capturar porque só analisa classificações relativas. O recuperador <code>linear</code> , combinado com a normalização, contabiliza corretamente as pontuações e suas diferenças, produzindo uma classificação mais significativa:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">doc1</td><td style="border: 1px solid #ccc; padding: 4px;">doc2</td><td style="border: 1px solid #ccc; padding: 4px;">doc3</td><td style="border: 1px solid #ccc; padding: 4px;">doc4</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">knn</td><td style="border: 1px solid #ccc; padding: 4px;">0,347</td><td style="border: 1px solid #ccc; padding: 4px;">0,35</td><td style="border: 1px solid #ccc; padding: 4px;">0,348</td><td style="border: 1px solid #ccc; padding: 4px;">0,346</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">bm25</td><td style="border: 1px solid #ccc; padding: 4px;">1</td><td style="border: 1px solid #ccc; padding: 4px;">0,01</td><td style="border: 1px solid #ccc; padding: 4px;">0,005</td><td style="border: 1px solid #ccc; padding: 4px;">0</td>
                    </tr>
              </tbody>
            </table>
          <p>Como podemos ver acima, a ótima classificação do doc1 e <code>score</code> para <code>bm25</code> são devidamente contabilizadas e refletidas nas pontuações finais. Além disso, todas as pontuações agora estão no intervalo <code>[0, 1]</code> para que possamos compará-las e combiná-las de uma forma muito mais intuitiva (e até mesmo criar processos de otimização offline).</p><h2>Juntando tudo</h2><p>Para aproveitar ao máximo o recuperador <code>linear</code> com normalização, a solicitação de pesquisa ficaria assim:</p><pre><code>GET linear_retriever_blog/_search
{
   "retriever": {
       "linear": {
           "retrievers": [
               {
                   "retriever": {
                       "knn": {
                          ...
                        }
                    },
                   "weight": 5
               },
                  {
                   "retriever": {
                       "standard": {
                          ...
                        }
                    },
                   "weight": 1.5,
                   "normalizer": "minmax"
               },


           ]
       }
   }
}</code></pre><p>Essa abordagem combina o melhor dos dois mundos: ela mantém a flexibilidade e a pontuação intuitiva do recuperador <code>linear</code> , ao mesmo tempo em que garante uma escala de pontuação consistente com a normalização MinMax.</p><p>Assim como todos os nossos recuperadores, o recuperador <code>linear</code> pode ser integrado a qualquer nível de uma árvore hierárquica de recuperadores, com suporte para explicabilidade, destaque de correspondência, recolhimento de campo e muito mais.</p><h2>Quando escolher o retriever linear e por que isso faz a diferença</h2><p>O recuperador <code>linear</code> :</p><ul><li>Preserva a importância relativa aproveitando pontuações reais, não apenas classificações.</li><li>Permite ajustes finos com contribuições ponderadas de diferentes consultas.</li><li>Melhora a consistência usando a normalização, tornando a pesquisa híbrida mais robusta e previsível.</li></ul><h2>Conclusão</h2><p>O recuperador <code>linear</code> já está disponível no Elasticsearch Serverless e nas versões 8.18 e 9.0! Mais exemplos e parâmetros de configuração também podem ser encontrados em nossa documentação. Experimente e veja como ele pode melhorar sua experiência de pesquisa híbrida — aguardamos seu feedback. Boa busca!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criando listas de julgamento com Quepid]]></title>
            <description><![CDATA[Aprenda a criar listas de julgamento no Quepid usando um processo colaborativo de avaliador humano e utilize os benchmarks para ajustar sua relevância.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/quepid-judgement-lists</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/quepid-judgement-lists</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Daniel Wrigley]]></dc:creator>
            <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/307c1774bd31f92bb4aa7b69e1a6796240465100-1600x914.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A criação de <a href="https://www.elastic.co/search-labs/blog/judgment-lists">listas de julgamento</a> é uma etapa crucial na otimização da qualidade dos resultados de pesquisa, mas pode ser uma tarefa complexa e difícil. Uma lista de julgamento é um conjunto selecionado de consultas de pesquisa combinadas com classificações de relevância para seus respectivos resultados, também conhecida como coleção de teste. As métricas calculadas usando esta lista servem como referência para medir o desempenho de um mecanismo de busca. Para ajudar a agilizar o processo de criação de listas de julgamento, a equipe <a href="https://opensourceconnections.com/">do OpenSource Connections</a> desenvolveu <a href="https://quepidapp.com/">o Quepid</a>. O julgamento pode ser explícito ou baseado em feedback implícito dos usuários. Este blog irá orientá-lo na configuração de um ambiente colaborativo no Quepid para permitir que avaliadores humanos façam julgamentos explícitos de forma eficaz, o que é a base de qualquer lista de julgamentos.</p><p>A Quepid auxilia as equipes de busca no processo de avaliação da qualidade da pesquisa:</p><ul><li>Criar conjuntos de consultas</li><li>Criar listas de julgamento</li><li>Calcular métricas de qualidade de pesquisa</li><li>Compare diferentes algoritmos/classificadores de busca com base em métricas de qualidade de busca calculadas.</li></ul><p>Para o nosso blog, vamos supor que administramos uma locadora de filmes e que nosso objetivo é melhorar a qualidade dos nossos resultados de busca.</p><h2>Pré-requisitos</h2><p>Este blog utiliza os dados e os mapeamentos do <a href="https://github.com/o19s/es-tmdb">repositório es-tmdb</a>. Os dados são do <a href="https://www.themoviedb.org/">The Movie Database</a>. Para acompanhar, crie um índice chamado tmdb com os mapeamentos e indexe os dados. Não importa se você configurar uma instância local ou usar uma implantação do Elastic Cloud para isso - qualquer uma funciona bem. Para este blog, pressupomos uma implementação no Elastic Cloud. Você pode encontrar informações sobre como indexar os dados no <a href="https://github.com/o19s/es-tmdb/blob/master/README.md">arquivo README do repositório es-tmdb</a>.</p><p>Faça uma consulta de correspondência simples no campo de título para <code>rocky</code> para confirmar que você tem dados para pesquisar:</p><pre><code>GET tmdb/_search
{
 "query": {
   "match": {
     "title": "rocky"
   }
 }
}</code></pre><p>Você deverá ver 8 resultados.</p><pre><code>{
 "took": 2,
 "timed_out": false,
 "_shards": {
   "total": 1,
   "successful": 1,
   "skipped": 0,
   "failed": 0
 },
 "hits": {
   "total": {
     "value": 8,
     "relation": "eq"
   }
…
}</code></pre><h2>Faça login no Quepid</h2><p><a href="https://github.com/o19s/quepid">O Quepid</a> é uma ferramenta que permite aos usuários medir a qualidade dos resultados de pesquisa e executar experimentos offline para melhorá-la.</p><p>Você pode usar o Quepid de duas maneiras: usando a versão gratuita e disponível publicamente em <a href="https://app.quepid.com">https://app.quepid.com</a>, ou instale o Quepid em uma máquina à qual você tenha acesso. Este post pressupõe que você esteja usando a versão gratuita hospedada. Se você deseja configurar uma instância do Quepid em seu ambiente, siga o <a href="https://github.com/o19s/quepid/wiki/Installation-Guide">Guia de Instalação</a>.</p><p>Independentemente da configuração escolhida, você precisará criar uma conta, caso ainda não tenha uma.</p><h2>Como configurar um caso do Quepid</h2><p>O Quepid é organizado em torno de &quot;Casos&quot;. Um Case armazena consultas juntamente com configurações de ajuste de relevância e instruções sobre como estabelecer uma conexão com seu mecanismo de busca.</p><ul><li>Para usuários iniciantes, selecione <strong>Criar seu primeiro caso de relevância</strong>.</li><li>Usuários recorrentes podem selecionar <strong>Casos de Relevância</strong> no menu principal e clicar em <strong>+ Criar um caso</strong>.</li></ul><p>Dê um nome descritivo ao seu caso, por exemplo, &quot;Linha de Base da Busca de Filmes&quot;, pois queremos começar a medir e aprimorar nossa busca de referência.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f9e480d9aae0d706cfc5371e41f19c706dd452a-594x251.png" style="width: 100%;" /><p>Confirme o nome selecionando <strong>Continuar</strong>.</p><p>Em seguida, estabelecemos uma conexão do Quepid com o mecanismo de busca. O Quepid pode se conectar a uma variedade de mecanismos de busca, incluindo o Elasticsearch.</p><p>A configuração irá variar dependendo da sua instalação do Elasticsearch e do Quepid. Para conectar o Quepid a uma implementação do Elastic Cloud, precisamos habilitar e configurar o CORS para nossa implementação do Elastic Cloud e ter uma chave de API pronta. Instruções detalhadas estão disponíveis no <a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">guia correspondente na documentação do Quepid</a>.</p><p>Insira as informações do seu endpoint Elasticsearch (<code>https://YOUR_ES_HOST:PORT/tmdb/_search</code>) e quaisquer informações adicionais necessárias para conectar (a chave da API no caso de uma implantação do Elastic Cloud nas opções de configuração <strong>avançadas</strong> ), teste a conexão clicando em <strong>ping</strong> e selecione <strong>Continuar</strong> para ir para a próxima etapa.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/267339dfaecae2740eb2ee2739bdc971608bdb5f-588x1169.png" style="width: 100%;" /><p>Agora definimos quais campos queremos que sejam exibidos no caso. Selecione todas as opções que ajudarão nossos avaliadores humanos a avaliar posteriormente a relevância de um documento para uma determinada consulta.</p><p>Defina <code>title</code> como o <em>Campo de Título</em>, deixe <code>_id</code> como o <em>Campo de ID</em> e adicione <code>overview, tagline, cast, vote_average, thumb:poster_path</code> como <em>Campos de Exibição Adicionais</em>. A última entrada exibe pequenas imagens em miniatura dos filmes em nossos resultados para nos guiar visualmente, assim como aos avaliadores humanos.</p><p>Confirme as configurações de exibição selecionando o botão <strong>Continuar</strong> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/02c58aae8c2ebb6d31f538b27462b4c65428fdc3-594x493.png" style="width: 100%;" /><p>O último passo é adicionar consultas de pesquisa ao caso. Adicione as três consultas <em>star wars</em>, <em>harrison ford</em> e <em>best action movie</em> uma de cada vez através do campo de entrada e <strong>clique em Continuar</strong>.</p><p>Idealmente, um caso contém consultas que representam consultas reais de usuários e ilustram diferentes tipos de consultas. Por ora, podemos imaginar <em>&quot;Star Wars&quot;</em> como uma consulta que representa todas as buscas por títulos de filmes, <em>&quot;Harrison Ford&quot; como</em> uma consulta que representa todas as buscas por membros do elenco e <em>&quot;Melhor Filme de Ação&quot;</em> como uma consulta que representa todas as buscas por filmes de um gênero específico. Isso geralmente é chamado de conjunto de consultas.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/baccfe96766319aa7255e9bff08913ac87d1517f-595x326.png" style="width: 100%;" /><p>Em um cenário de produção, amostraríamos consultas de dados de rastreamento de eventos aplicando técnicas estatísticas como <a href="https://opensourceconnections.com/blog/2022/10/13/how-to-succeed-with-explicit-relevance-evaluation-using-probability-proportional-to-size-sampling/">a amostragem de Probabilidade Proporcional ao Tamanho</a> e importaríamos essas consultas amostradas para o Quepid para incluir consultas do início (consultas frequentes) e da cauda (consultas infrequentes) em relação à sua frequência, o que significa que damos preferência a consultas mais frequentes sem excluir as raras.</p><p>Por fim, selecione <strong>Concluir</strong> e você será redirecionado para a interface do caso, onde verá as três consultas definidas.</p><h2>Consultas e necessidades de informação</h2><p>Para atingirmos nosso objetivo principal de criar uma lista de julgamentos, avaliadores humanos precisarão julgar um resultado de busca (normalmente um documento) para uma determinada consulta. Isso é chamado de par consulta/documento.</p><p>Às vezes, parece fácil saber o que um usuário queria ao analisar a consulta. A intenção por trás da consulta <code>harrison ford</code> é encontrar filmes estrelados por Harrison Ford, o ator. E quanto à consulta <code>action</code>? Sei que eu teria a tentação de dizer que a intenção do usuário é encontrar filmes do gênero ação. Mas quais? Os mais recentes, os mais populares, os melhores de acordo com as avaliações dos usuários? Ou será que o usuário quer encontrar todos os filmes que se chamam &quot;Ação&quot;? <a href="https://www.themoviedb.org/search/movie?query=Action">Existem pelo menos 12 (!) filmes chamados “Action” no The Movie Database</a> e seus nomes diferem principalmente no número de pontos de exclamação no título.</p><p>Dois avaliadores humanos podem divergir na interpretação de uma pergunta cuja intenção não seja clara. Entenda a Necessidade de Informação: Uma <a href="https://en.wikipedia.org/wiki/Information_needs">Necessidade de Informação</a> é um desejo consciente ou inconsciente por informação. Definir uma necessidade de informação ajuda os avaliadores humanos a julgarem os documentos em relação a uma consulta, desempenhando, portanto, um papel importante no processo de elaboração de listas de julgamento. Usuários experientes ou especialistas no assunto são bons candidatos para especificar as necessidades de informação. É uma boa prática definir as necessidades de informação a partir da perspectiva do usuário, pois são essas necessidades que os resultados da busca devem satisfazer.</p><p>Necessidades de informação para as consultas do nosso caso de “Linha de Base de Pesquisa de Filmes”:</p><ol><li><strong>Star Wars</strong>: O usuário deseja encontrar filmes ou séries da franquia Star Wars. Documentários sobre Star Wars podem ser relevantes.</li><li><strong>Harrison Ford</strong>: O usuário deseja encontrar filmes estrelados pelo ator Harrison Ford. Filmes em que Harrison Ford desempenha um papel diferente, como o de narrador, podem ser relevantes.</li><li><strong>Melhor filme de ação</strong>: O usuário deseja encontrar filmes de ação, de preferência aqueles com alta média de votos dos usuários.</li></ol><h2>Como definir necessidades de informação no Quepid</h2><p>Para definir uma necessidade de informação no Quepid, acesse a interface do caso:</p><p>1. Abra uma pesquisa (por exemplo, <em>star wars</em>) e selecione <em>Alternar notas.</em></p><p>2. Insira a necessidade de informação no primeiro campo e quaisquer observações adicionais no segundo campo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e01d3d5242a350d8797faa665eb3170039f5dfa2-1483x559.png" style="width: 100%;" /><p>3. Clique em <strong>Salvar</strong>.</p><p>Para um pequeno número de consultas, esse processo funciona bem. No entanto, ao expandir seu caso de três para 100 consultas (os casos do Quepid geralmente variam de 50 a 100 consultas), você pode querer definir as necessidades de informação fora do Quepid (por exemplo, em uma planilha) e, em seguida, carregá-las por meio da <strong>opção Importar</strong> e selecionar <strong>Necessidades de Informação</strong>.</p><h2>Criar uma equipe no Quepid e compartilhar seu caso</h2><p>Julgamentos colaborativos melhoram a qualidade das avaliações de relevância. Para formar uma equipe:</p><p>1. Navegue até <strong>&quot;Equipes&quot;</strong> no menu principal.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/797706e8d130b474a95d30b6fa22ecaf36f98c03-613x58.png" style="width: 100%;" /><p>2. Clique em <strong>+ Adicionar novo</strong>, insira um nome para a equipe (por exemplo, &quot;Avaliadores de relevância de pesquisa&quot;) e clique em <strong>Criar</strong>.</p><p>3. Adicione membros digitando seus endereços de e-mail e clicando em <strong>Adicionar Usuário</strong>.</p><p>4. Na interface do caso, selecione <strong>Compartilhar caso</strong>.</p><p>5. Selecione a equipe apropriada e confirme.</p><h2>Criar um livro de julgamentos no Quepid</h2><p>Um livro no Quepid permite que vários avaliadores avaliem pares de consulta/documento de forma sistemática. Para criar um:</p><p>1. Na interface do processo, acesse <strong>Julgamentos</strong> e clique em <strong>+ Criar um Livro</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a077f26225961150b7414463d7db04f090b68d6-896x365.png" style="width: 100%;" /><p>2. Configure o livro com um nome descritivo, atribua-o à sua equipe, selecione um método de pontuação (por exemplo, DCG@10) e defina a estratégia de seleção (avaliadores únicos ou múltiplos). Utilize as seguintes configurações para o livro:</p><ul><li><strong>Nome</strong>: “Pesquisa de Filmes em Escala de 0 a 3”</li><li><strong>Equipes com as quais você deseja compartilhar este livro</strong>: Marque a caixa da equipe que você criou.</li><li><strong>Marcador</strong>: DCG@10</li></ul><p>3. Clique em <strong>Criar livro.</strong></p><p>O nome é descritivo e contém informações sobre o que é pesquisado em (“Filmes”) e também a escala das avaliações (“0-3”). O Scorer DCG@10 selecionado define a forma como a métrica de pesquisa será calculada. “DCG” é a abreviação de <a href="https://en.wikipedia.org/wiki/Discounted_cumulative_gain">Ganho Cumulativo Descontado</a> e “@10” é o número de resultados do topo considerados no cálculo da métrica.</p><p>Neste caso, estamos usando uma métrica que mede o ganho de informação e o combina com a ponderação posicional. Pode haver outras métricas de pesquisa mais adequadas ao seu caso de uso, e <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric">escolher a correta é um desafio por si só</a>.</p><h2>Preencha o livro com pares de consulta/documento</h2><p>Para adicionar pares de consulta/documento para avaliação de relevância, siga estes passos:</p><p>1. Na interface do processo, navegue até &quot;Sentenças&quot;.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" style="width: 100%;" /><p>2. Selecione o livro que você criou.</p><p>3. Clique em &quot;Preencher Livro&quot; e confirme selecionando &quot;Atualizar Pares de Consulta/Documento para o Livro&quot;.</p><p>Esta ação gera pares com base nos principais resultados de pesquisa para cada consulta, prontos para avaliação pela sua equipe.</p><h2>Deixar sua equipe de avaliadores humanos julgar </h2><p>Até o momento, as etapas concluídas foram de natureza bastante técnica e administrativa. Agora que essa preparação necessária foi concluída, podemos deixar nossa equipe de juízes fazer seu trabalho. Em essência, a função do juiz é avaliar a relevância de um determinado documento para uma questão específica. O resultado desse processo é a lista de julgamentos, que contém todos os rótulos de relevância para os pares de documentos de consulta avaliados. A seguir, esse processo e sua interface serão explicados com mais detalhes.</p><h3>Visão geral da interface Human Rating</h3><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/be3016091b49655dab3354d84e6dc638f3468390-1283x664.png" style="width: 100%;" /><p>A interface de Avaliação Humana do Quepid foi projetada para avaliações eficientes:</p><ul><li><strong>Consulta:</strong> Exibe o termo de pesquisa.</li><li><strong>Necessidade de informação:</strong> Mostra a intenção do usuário.</li><li><strong>Diretrizes de pontuação:</strong> Fornece instruções para avaliações consistentes.</li><li><strong>Metadados do documento:</strong> Apresentam detalhes relevantes sobre o documento.</li><li><strong>Botões de avaliação:</strong> Permitem que os avaliadores atribuam julgamentos com os respectivos atalhos de teclado.</li></ul><h3>Usando a interface de Human Rating</h3><p>Como avaliador humano, acesso a interface através da visão geral do livro:</p><p>1. Navegue até a interface do caso e clique em <strong>Julgamentos</strong>.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d717c5b06ae6cb42ed2b9e771486a12f738a9890-1041x218.png" style="width: 100%;" /><p>2. Clique em <strong>Mais avaliações são necessárias!</strong></p><p>O sistema apresentará um par consulta/documento que ainda não foi avaliado e que requer julgamentos adicionais. Isso é determinado pela estratégia de seleção do livro:</p><ul><li><em>Avaliador único</em>: Um único julgamento por par consulta/documento.</li><li><em>Avaliadores Múltiplos</em>: Até três avaliações por par consulta/documento.</li></ul><h3>Avaliando pares de consulta/documento</h3><p>Vamos analisar alguns exemplos. Ao seguir este guia, você provavelmente se deparará com diferentes filmes. No entanto, os princípios de classificação permanecem os mesmos.</p><p>Nosso primeiro exemplo é o filme “Heroes” para a consulta <em>harrison ford</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c1e14b0d8b04dd579471932dbe4ff72ae5692a02-981x571.png" style="width: 100%;" /><p>Primeiro analisamos a consulta, depois a necessidade de informação e, em seguida, avaliamos o filme com base nos metadados fornecidos.</p><p>Este filme é um resultado relevante para nossa pesquisa, já que Harrison Ford faz parte do elenco. Podemos considerar os filmes mais recentes como subjetivamente mais relevantes, mas isso não faz parte da nossa necessidade de informação. Assim, classificamos este documento como &quot;Perfeito&quot;, o que corresponde a um 3 em nossa escala de notas.</p><p>Nosso próximo exemplo é o filme “Ford vs Ferrari” para a pesquisa <em>“Harrison Ford”</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/052af7894506d7a765af156ba8e26ceec3559973-981x789.png" style="width: 100%;" /><p>Seguindo a mesma prática, avaliamos esta consulta/documento analisando a consulta, a necessidade de informação e, em seguida, o quão bem os metadados do documento correspondem à necessidade de informação.</p><p>Este é um resultado ruim. Provavelmente vemos esse resultado porque um dos nossos termos de pesquisa, &quot;ford&quot;, corresponde ao título. Mas Harrison Ford não desempenha nenhum papel neste filme, nem em nenhum outro. Assim, classificamos este documento como &quot;Ruim&quot;, o que corresponde a 0 em nossa escala de notas.</p><p>Nosso terceiro exemplo é o filme “Action Jackson” para a busca <em>“melhor filme de ação</em>”:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/247ab862fbc7435537709f8c96619cb331133d09-985x606.png" style="width: 100%;" /><p>Parece um filme de ação, então a necessidade de informação está pelo menos parcialmente satisfeita. No entanto, a média dos votos é de 5,4 em 10. E isso faz com que este filme provavelmente não seja o melhor filme de ação da nossa coleção. Isso me levaria, como juiz, a classificar este documento como &quot;Razoável&quot;, o que corresponde a 1 em nossa escala de classificação.</p><p>Esses exemplos ilustram o processo de avaliação de pares de consulta/documento com o Quepid, em particular, em um nível mais alto e também em geral.</p><h2>Práticas recomendadas para avaliadores humanos</h2><p>Os exemplos apresentados podem dar a impressão de que é fácil chegar a julgamentos explícitos. Mas criar um programa confiável de avaliação humana não é tarefa fácil. É um processo repleto de desafios que podem facilmente comprometer a qualidade dos seus dados:</p><ul><li>Os avaliadores humanos podem ficar fatigados devido a tarefas repetitivas.</li><li>Preferências pessoais podem distorcer julgamentos.</li><li>O nível de conhecimento especializado varia de juiz para juiz.</li><li>Os avaliadores frequentemente precisam conciliar múltiplas responsabilidades.</li><li>A relevância percebida de um documento pode não corresponder à sua real relevância para uma consulta.</li></ul><p>Esses fatores podem resultar em julgamentos inconsistentes e de baixa qualidade. Mas não se preocupe – existem práticas recomendadas comprovadas que podem ajudá-lo a minimizar esses problemas e a construir um processo de avaliação mais robusto e confiável:</p><ul><li><strong>Avaliação consistente:</strong> Analise a consulta, a necessidade de informação e os metadados do documento em ordem.</li><li><strong>Consulte as diretrizes:</strong> Utilize as diretrizes de pontuação para manter a consistência. As diretrizes de avaliação podem conter exemplos de quando aplicar cada nota, ilustrando o processo de julgamento. Realizar uma consulta com avaliadores humanos após o primeiro lote de julgamentos provou ser uma boa prática para identificar casos extremos desafiadores e onde é necessário suporte adicional.</li><li><strong>Utilize as opções:</strong> Em caso de dúvida, use &quot;Vou avaliar depois&quot; ou &quot;Não sei dizer&quot;, fornecendo explicações quando necessário.</li><li><strong>Faça pausas:</strong> Pausas regulares ajudam a manter a qualidade do julgamento. O Quepid ajuda nas pausas regulares, lançando confetes sempre que um avaliador humano termina um lote de julgamentos.</li></ul><p>Seguindo esses passos, você estabelece uma abordagem estruturada e colaborativa para a criação de listas de julgamento no Quepid, aumentando a eficácia dos seus esforços de otimização da relevância da busca.</p><h2>Próximas etapas</h2><p>Para onde ir a partir daqui? As listas de julgamento são apenas um passo fundamental para melhorar a qualidade dos resultados de pesquisa. Eis os próximos passos:</p><h3>Calcule métricas e comece a experimentar</h3><p>Uma vez que as listas de avaliações estejam disponíveis, aproveitar essas avaliações e calcular <a href="https://opensourceconnections.com/blog/2020/02/28/choosing-your-search-relevance-metric/">as métricas de qualidade da busca</a> é uma progressão natural. O Quepid calcula automaticamente a métrica configurada para o caso atual quando os julgamentos estão disponíveis. As métricas são implementadas como &quot;Pontuadores&quot; e você pode fornecer as suas próprias caso as opções suportadas não incluam a sua favorita!</p><p>Acesse a interface do caso, navegue até <strong>Selecionar Avaliador</strong>, escolha <em>DCG@10</em> e confirme clicando em <strong>Selecionar Avaliador</strong>. O Quepid agora calculará o DCG@10 por consulta e também a média geral das consultas para quantificar a qualidade dos resultados da pesquisa para o seu caso.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0ff3b9918342071744d681a43d542102e927abd3-1163x551.png" style="width: 100%;" /><p>Agora que a qualidade dos seus resultados de pesquisa foi quantificada, você pode executar os primeiros experimentos. A experimentação começa com a geração de hipóteses. Ao analisar as três consultas na captura de tela após classificá-las, fica evidente que elas apresentam desempenhos muito diferentes em termos de qualidade de busca: <em>&quot;Star Wars&quot;</em> tem um desempenho bastante bom, <em>&quot;Harrison Ford&quot;</em> parece razoável, mas o maior potencial reside em <em>&quot;Melhor Filme de Ação&quot;</em>.</p><p>Expandindo essa consulta, vemos seus resultados e podemos mergulhar nos detalhes minuciosos, explorando por que os documentos corresponderam e o que influencia suas pontuações:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c45c6cae085a492198c0f8b7060a1a7204e3724e-1131x691.png" style="width: 100%;" /><p>Ao clicar em “Explicar consulta” e acessar a guia “Análise”, vemos que a consulta é uma DisjunctionMaxxQuery que pesquisa em três campos: <em>cast</em>, <em>overview</em> e <em>title</em>:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4a1e1bb2a9cd28e9c48e0ba16357d17ed9d3a5cf-894x557.png" style="width: 100%;" /><p>Normalmente, como engenheiros de busca, conhecemos alguns detalhes específicos do domínio da nossa plataforma de busca. Nesse caso, podemos saber que temos um campo <em>de gêneros</em> . Vamos adicionar isso à consulta e ver se a qualidade da pesquisa melhora.</p><p>Usamos o <strong>ambiente de testes de consulta (Query Sandbox)</strong> que é aberto ao selecionar <strong>&quot;Ajustar Relevância&quot;</strong> na interface do caso. Explore esta opção adicionando o campo <em>de gêneros</em> que você pesquisou:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "#$query##",
      "type": "best_fields",
      "fields": [
        "title^10",
        "overview",
        "cast",
        "genres"
      ]
    }
  }
}</code></pre><p>Clique em &quot;Executar minhas pesquisas novamente&quot;! E veja os resultados. Será que mudaram? Infelizmente não. Agora temos muitas opções para explorar, basicamente todas as opções de consulta que o Elasticsearch oferece:</p><ul><li>Poderíamos aumentar o peso do campo de gêneros.</li><li>Poderíamos adicionar uma função que aumentasse a relevância dos documentos com base na média de votos.</li><li>Poderíamos criar uma consulta mais complexa que priorizasse documentos com base na média de votos apenas se houvesse uma forte correspondência de gêneros.</li><li>…</li></ul><p>A melhor coisa de ter todas essas opções e explorá-las no Quepid é que temos uma maneira de quantificar os efeitos não apenas na consulta específica que estamos tentando melhorar, mas em todas as consultas que temos em nosso caso. Isso nos impede de melhorar uma consulta com baixo desempenho sacrificando a qualidade dos resultados de pesquisa em outras consultas. Podemos iterar de forma rápida e barata e validar o valor de nossa hipótese sem qualquer risco, tornando a experimentação offline uma capacidade fundamental de todas as equipes de busca.</p><h3>Medir a confiabilidade entre avaliadores</h3><p>Mesmo com descrições de tarefas, necessidades de informação e uma interface de avaliação humana como a que a Quepid oferece, os avaliadores humanos podem discordar.</p><p>A discordância em si não é algo ruim, muito pelo contrário: medir a discordância pode revelar problemas que você talvez queira abordar. A relevância pode ser subjetiva, as consultas podem ser ambíguas e os dados podem estar incompletos ou incorretos. <a href="https://en.wikipedia.org/wiki/Fleiss%27_kappa">O coeficiente Kappa de Fleiss</a> é uma medida estatística de concordância entre avaliadores, e existe um exemplo de planilha no Quepid que você pode usar. Para encontrá-lo, selecione <strong>Notebooks</strong> na navegação de nível superior e selecione o notebook <strong>Fleiss Kappa.ipynb</strong> na pasta <strong>examples</strong> .</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f0669ae96371368ef4d84bb28669560ef09d755c-624x61.png" style="width: 100%;" /><h2>Conclusão</h2><p>O Quepid permite que você enfrente até mesmo os desafios mais complexos de relevância de pesquisa e continua a evoluir: <a href="https://github.com/o19s/quepid/blob/main/CHANGELOG.md#800----2024-02-14">a partir da versão 8, o Quepid oferece suporte a julgamentos gerados por IA</a>, o que é particularmente útil para equipes que desejam dimensionar seu processo de geração de julgamentos.</p><p>Os fluxos de trabalho do Quepid permitem criar listas de julgamento escaláveis de forma eficiente, o que resulta em resultados de pesquisa que realmente atendem às necessidades do usuário. Com as listas de critérios de avaliação estabelecidas, você terá uma base sólida para medir a relevância da pesquisa, implementar melhorias e proporcionar melhores experiências ao usuário.</p><p>Ao prosseguir, lembre-se de que o ajuste de relevância é um processo contínuo. Listas de avaliação permitem que você avalie seu progresso de forma sistemática, mas são mais eficazes quando combinadas com experimentação, análise de métricas e melhorias iterativas.</p><h2>Para ler mais</h2><ul><li>Documentação Quepid:<ul><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/32/relevancy-is-a-team-sport">Relevância é um esporte coletivo</a></li><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/18/quepid-for-human-raters">Quépid para avaliadores humanos</a></li><li><a href="https://quepid-docs.dev.o19s.com/2/quepid/49/how-to-connect-quepid-to-elastic-cloud">Como conectar o Quepid ao Elastic Cloud</a></li></ul></li><li><a href="https://github.com/o19s/quepid">Repositório Quepid no Github</a></li><li><a href="https://opensourceconnections.com/blog/2020/07/07/meet-pete-the-e-commerce-search-product-manager/">Conheça Pete, uma série de posts no blog sobre como melhorar a busca em e-commerce.</a></li><li><a href="https://opensourceconnections.com/slack">Slack de Relevância</a>: entre no canal #quepid</li></ul><p><strong>Faça parceria com <a href="https://opensourceconnections.com/">a Open Source Connections</a></strong> para transformar suas capacidades de busca e IA e capacitar sua equipe a evoluí-las continuamente. Nosso histórico comprovado abrange o mundo todo, com clientes alcançando consistentemente melhorias significativas na qualidade da busca, na capacidade da equipe e no desempenho dos negócios. <a href="https://opensourceconnections.com/contact/">Entre em contato conosco hoje mesmo</a> para saber mais.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exibindo campos em um índice do Elasticsearch]]></title>
            <description><![CDATA[Explorando técnicas para exibir campos em um índice do Elasticsearch.
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/displaying-fields-in-an-elasticsearch-index</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/displaying-fields-in-an-elasticsearch-index</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Kofi Bartlett]]></dc:creator>
            <pubDate>Mon, 26 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fa386d7bbaeab6855e62897ace8d7dca91a060b4-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Neste artigo, discutiremos como exibir campos em um índice do Elasticsearch. Isso pode ser útil para entender a estrutura dos seus dados, identificar campos específicos e solucionar problemas. Abordaremos os seguintes tópicos:</p><ol><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#1.-using-the--mapping-api-to-retrieve-field-information">Utilizando a  API<code>_mapping</code></a>para recuperar informações de campo</li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#2.-using-the--search-api-to-display-field-values">Utilizando a  API<code>_search</code></a>para exibir valores de campo</li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#3.-filtering-fields-using-the-fields-parameter">Filtrar campos usando o  parâmetro<code>fields</code></a></li><li><a href="https://www.elastic.co/search-labs/blog/displaying-fields-in-an-elasticsearch-index#4.-displaying-nested-fields">Exibindo campos aninhados</a></li></ol><h2>1. Utilizando a API _mapping para recuperar informações de campo</h2><p>A API <code>_mapping</code> permite recuperar a definição de mapeamento para um índice ou vários <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-index/">índices</a>. Isso inclui informações sobre os campos, seus tipos de dados e outras propriedades. Para recuperar o mapeamento de um índice específico, utilize a seguinte solicitação:</p><pre><code>GET /<index_name>/_mapping</code></pre><p>Por exemplo, se você tiver um índice chamado <code>my_index</code>, poderá recuperar seu mapeamento com a seguinte solicitação:</p><pre><code>GET /my_index/_mapping</code></pre><p>A resposta incluirá a definição de mapeamento para o índice, que contém informações sobre os campos e suas propriedades.</p><p>Também é possível recuperar o mapeamento de um campo específico. Isso pode ser útil se o seu mapeamento for muito extenso e você quiser se concentrar apenas em um campo específico. Para obter o mapeamento de um campo específico, utilize a seguinte solicitação:</p><pre><code>GET /my_index/_mapping/field/my_field</code></pre><p>Você também pode recuperar os mapeamentos de vários campos separando seus nomes com vírgulas, como na seguinte solicitação:</p><pre><code>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3</code></pre><h2>2. Usando a API _search para exibir valores de campo</h2><p>Para exibir os valores dos campos em um índice do Elasticsearch, você pode usar a API <code>_search</code> . Por padrão, a API <code>_search</code> retorna o campo <code>_source</code> , que contém o documento JSON original que foi indexado. Para exibir apenas campos específicos, você pode usar o parâmetro <code>_source</code> na solicitação de pesquisa.</p><p>Aqui está um exemplo de uma solicitação de pesquisa que retorna os valores dos campos <code>title</code> e <code>author</code> para documentos no índice <code>my_index</code> :</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}</code></pre><p>Neste exemplo, o parâmetro <code>_source</code> especifica os campos a serem retornados.</p><h2>3. Filtrar campos usando o parâmetro fields</h2><p>Você também pode usar o parâmetro <code>fields</code> para filtrar os campos retornados na resposta da pesquisa. Isso pode ser útil se você precisar apenas de campos específicos e quiser reduzir o tamanho da resposta. O parâmetro <code>fields</code> aceita uma matriz de nomes de campos ou padrões curinga.</p><p>Por exemplo, para retornar apenas os campos <code>title</code> e <code>author</code> para documentos no índice <code>my_index</code> , você pode usar a seguinte solicitação de pesquisa:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}</code></pre><p>Note que o parâmetro <code>_source</code> está definido como falso para não retornar o documento de origem.</p><p>Para retornar todos os campos com o tipo de dados <code>text</code> , você pode usar um padrão curinga como este:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["*.text"],
  "_source": false
}</code></pre><h2>4. Exibição de campos aninhados</h2><p>Se o seu índice contiver campos aninhados, você pode usar a notação de ponto para especificar o caminho do campo aninhado no parâmetro <code>fields</code> . Por exemplo, se você tiver um campo aninhado chamado <code>address.city</code>, poderá incluí-lo na resposta da pesquisa desta forma:</p><pre><code>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}</code></pre><p>Neste exemplo, a resposta da pesquisa incluirá os valores dos campos <code>title</code>, <code>author</code> e <code>address.city</code> .</p><h2>Conclusão</h2><p>Em conclusão, a exibição de campos em um índice do Elasticsearch pode ser realizada usando a API <code>_mapping</code> para recuperar informações do campo e a API <code>_search</code> para exibir os valores do campo. Você pode filtrar os campos retornados na resposta da pesquisa usando os parâmetros <code>_source</code> ou <code>fields</code> e exibir campos aninhados usando a notação de ponto. Essas técnicas podem ajudá-lo a entender a estrutura de seus dados, identificar campos específicos e solucionar problemas.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Spring AI e Elasticsearch como seu banco de dados vetorial]]></title>
            <description><![CDATA[Aprenda a criar um app RAG pronto para produção usando Spring AI e Elasticsearch e a integrar LLMs aos seus dados com um banco de dados vetorial.
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/spring-ai-elasticsearch-application</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/spring-ai-elasticsearch-application</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Philipp Krenn, Laura Trotta]]></dc:creator>
            <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0771fb5b3d9234697cb868cd7d9d1b840000bf29-1280x720.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p><strong>O Spring AI</strong> já está disponível para o público em geral, com sua primeira <a href="https://spring.io/blog/2025/05/20/spring-ai-1-0-GA-released">versão estável, a 1.0,</a> pronta para download no <a href="https://mvnrepository.com/artifact/org.springframework.ai/spring-ai-core">Maven Central</a>. Vamos usá-lo imediatamente para construir um aplicativo de IA completo, usando seu <a href="https://www.elastic.co/what-is/large-language-models">LLM</a> favorito e nosso <a href="https://www.elastic.co/elasticsearch/vector-database">banco de dados vetorial</a> favorito. Ou então, acesse diretamente o <a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">repositório</a> com a aplicação final.</p><a href="https://www.youtube.com/watch/ms2xRO8B1YU">Watch video on YouTube</a><h2>O que é Spring AI?</h2><p><strong>O Spring AI 1.0</strong>, uma solução abrangente para engenharia de IA em Java, já está disponível após um período significativo de desenvolvimento influenciado pelos rápidos avanços na área de IA. Esta versão inclui diversas novas funcionalidades essenciais para engenheiros de IA.</p><p>Java e Spring estão em uma posição privilegiada para aproveitar toda essa onda de IA. Inúmeras empresas estão executando seus sistemas com Spring Boot, o que torna extremamente fácil integrar IA ao que elas já fazem. Basicamente, você pode conectar sua lógica de negócios e seus dados diretamente a esses modelos de IA sem muita dificuldade.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/328d7c51e1c145e94ea1e73ee9ff91836d3b180e-1600x773.png" style="width: 100%;" /><p>O Spring AI oferece suporte a <a href="https://docs.spring.io/spring-ai/reference/api/index.html">diversos modelos e tecnologias de IA</a>, tais como:</p><ul><li><strong>Modelos de imagem</strong>: geram imagens a partir de instruções de texto.</li><li><strong>Modelos de transcrição</strong>: pegam fontes de áudio e as convertem em texto.</li><li><strong>Modelos de incorporação: </strong>convertem dados arbitrários em <a href="https://www.elastic.co/what-is/vector-embedding">vetores</a>, que são tipos de dados otimizados para busca de similaridade semântica.</li><li><strong>Modelos de bate-papo: </strong>estes<strong> </strong>Deve ser familiar! Você sem dúvida já teve até mesmo uma breve conversa com um deles em algum lugar.</li></ul><p>Os modelos de bate-papo parecem ser o foco da maior parte da atenção no campo da IA, e com razão, eles são incríveis! Você pode pedir a ajuda deles para corrigir um documento ou escrever um poema. (Só não peça para eles contarem uma piada... ainda.) Eles são incríveis, mas têm alguns problemas.</p><h2>Soluções de IA da Spring para desafios de IA</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/2ebd68a90ebc73847df6ef7325936d4d06b35c8c-1600x900.jpg" style="width: 100%;" /><p>Vamos analisar alguns desses problemas e suas soluções no Spring AI.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Problema</td><td style="border: 1px solid #ccc; padding: 4px;">Solução</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consistência</td><td style="border: 1px solid #ccc; padding: 4px;">Os modelos de bate-papo são de mente aberta e propensos a distrações.</td><td style="border: 1px solid #ccc; padding: 4px;">Você pode fornecer a eles um comando do sistema para controlar sua forma e estrutura geral.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Memória</td><td style="border: 1px solid #ccc; padding: 4px;">Os modelos de IA não têm memória, portanto não conseguem correlacionar uma mensagem de um determinado usuário com outra.</td><td style="border: 1px solid #ccc; padding: 4px;">Você pode fornecer a eles um sistema de memória para armazenar as partes relevantes da conversa.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Isolamento</td><td style="border: 1px solid #ccc; padding: 4px;">Os modelos de IA vivem em pequenos ambientes isolados, mas podem fazer coisas realmente incríveis se você lhes der acesso a ferramentas — funções que eles podem invocar quando julgarem necessário.</td><td style="border: 1px solid #ccc; padding: 4px;">O Spring AI oferece suporte à chamada de ferramentas, o que permite informar ao modelo de IA sobre as ferramentas em seu ambiente, que ele poderá então solicitar que você as invoque. Essa interação de múltiplas etapas é gerenciada de forma transparente para você.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Dados privados</td><td style="border: 1px solid #ccc; padding: 4px;">Os modelos de IA são inteligentes, mas não são oniscientes! Eles não sabem o que está contido em seus bancos de dados proprietários - e acreditamos que você também não gostaria que soubessem!</td><td style="border: 1px solid #ccc; padding: 4px;">Você precisa influenciar as respostas inserindo texto nos prompts — basicamente, usando o poderoso operador de concatenação de strings para adicionar texto à requisição antes que o modelo analise a pergunta feita. Informações adicionais, se quiser. Como você decide o que deve ser enviado e o que não deve? Utilize um armazenamento vetorial para selecionar apenas os dados relevantes e enviá-los adiante. Isso é chamado de geração aumentada por recuperação, ou RAG.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Alucinação</td><td style="border: 1px solid #ccc; padding: 4px;">Os modelos de bate-papo com IA gostam, bem, de conversar! E às vezes fazem isso com tanta confiança que chegam a inventar coisas.</td><td style="border: 1px solid #ccc; padding: 4px;">É necessário usar a avaliação — utilizando um modelo para validar a saída de outro — para confirmar resultados razoáveis.</td>
                    </tr>
              </tbody>
            </table>
          <p></p><p>E, claro, nenhuma aplicação de IA é uma ilha. Atualmente, os sistemas e serviços de IA modernos funcionam melhor quando integrados a outros sistemas e serviços. <strong><a href="https://modelcontextprotocol.io/introduction">Protocolo de Contexto do Modelo</a> </strong>(MCP) possibilita conectar seus aplicativos de IA com outros serviços baseados em MCP, independentemente da linguagem em que foram escritos. Você pode reunir tudo isso em fluxos de trabalho <strong>orientados a agentes </strong>que conduzem a um objetivo maior.</p><p>A melhor parte? Você pode fazer tudo isso enquanto aproveita os padrões e abstrações familiares que qualquer desenvolvedor Spring Boot já espera: dependências iniciais convenientes para praticamente tudo estão disponíveis no <strong><a href="https://start.spring.io">Spring Initializr</a>.</strong></p><p>O Spring AI oferece configurações automáticas convenientes do Spring Boot, proporcionando a abordagem de convenção sobre configuração que você já conhece e espera. E o Spring AI oferece suporte à observabilidade com o Actuator do Spring Boot e o projeto Micrometer. Ele também funciona bem com o GraalVM e threads virtuais, permitindo que você crie aplicativos de IA super rápidos, eficientes e escaláveis.</p><h2>Por que o Elasticsearch?</h2><p>O Elasticsearch é um mecanismo de busca de texto completo, você provavelmente já sabe disso. Então, por que estamos usando isso para este projeto? Bem, <em>também</em> é uma loja de vetores! E uma muito boa, por sinal, onde os dados ficam lado a lado com o texto completo. Outras vantagens notáveis:</p><ul><li>Super fácil de configurar</li><li>Código aberto</li><li>Escalável horizontalmente</li><li>A maior parte dos dados de formato livre da sua organização provavelmente já reside em um cluster Elasticsearch.</li><li>Funcionalidade completa de mecanismo de busca</li><li>Totalmente <a href="https://docs.spring.io/spring-ai/reference/api/vectordbs/elasticsearch.html">integrado ao Spring AI</a>!</li></ul><p>Considerando tudo, o Elasticsearch atende a todos os requisitos para ser um excelente banco de dados de vetores, então vamos configurá-lo e começar a construir nossa aplicação!</p><h2>Introdução ao Elasticsearch</h2><p>Precisaremos tanto do Elasticsearch quanto do Kibana, o console de interface do usuário que você usará para interagir com os dados hospedados no banco de dados.</p><p>Você pode experimentar tudo em sua máquina local graças aos recursos das imagens Docker e à <a href="http://elastic.co">página inicial da Elastic.co</a>. Acesse essa página, role a tela para baixo até encontrar o comando <code>curl</code> , execute-o e redirecione a saída diretamente para o seu shell:</p><pre><code> curl -fsSL https://elastic.co/start-local | sh 
  ______ _           _   _      
 |  ____| |         | | (_)     
 | |__  | | __ _ ___| |_ _  ___ 
 |  __| | |/ _` / __| __| |/ __|
 | |____| | (_| \__ \ |_| | (__ 
 |______|_|\__,_|___/\__|_|\___|
-------------------------------------------------
🚀 Run Elasticsearch and Kibana for local testing
-------------------------------------------------
ℹ️  Do not use this script in a production environment
⌛️ Setting up Elasticsearch and Kibana v9.0.0...
- Generated random passwords
- Created the elastic-start-local folder containing the files:
  - .env, with settings
  - docker-compose.yml, for Docker services
  - start/stop/uninstall commands
- Running docker compose up --wait
[+] Running 25/26
 ✔ kibana_settings Pulled                                                 16.7s 
 ✔ kibana Pulled                                                          26.8s 
 ✔ elasticsearch Pulled                                                   17.4s                                                                     
[+] Running 6/6
 ✔ Network elastic-start-local_default             Created                 0.0s 
 ✔ Volume "elastic-start-local_dev-elasticsearch"  Created                 0.0s 
 ✔ Volume "elastic-start-local_dev-kibana"         Created                 0.0s 
 ✔ Container es-local-dev                          Healthy                12.9s 
 ✔ Container kibana_settings                       Exited                 11.9s 
 ✔ Container kibana-local-dev                      Healthy                21.8s 
🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: w1GB15uQ
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: SERqaGlKWUJLNVJDODc1UGxjLWE6WFdxSTNvMU5SbVc5NDlKMEhpMzJmZw==
Learn more at https://github.com/elastic/start-local
➜  ~ </code></pre><p>Isso simplesmente baixará e configurará as imagens Docker para Elasticsearch e Kibana e, após alguns minutos, você as terá em execução em sua máquina local, com as credenciais de conexão já definidas.</p><p>Você também tem duas URLs diferentes que pode usar para interagir com sua instância do Elasticsearch. Faça como indicado e acesse <a href="http://localhost:5601">http://localhost:5601</a> pelo seu navegador.</p><p>Observe também o nome de usuário <code>elastic</code> e a senha impressos no console: você precisará deles para fazer login (no exemplo de saída acima, eles são respectivamente <code>elastic</code> e <code>w1GB15uQ</code>).</p><p></p><h2>Reunindo o aplicativo</h2><p>Acesse a página <a href="https://start.spring.io">do Spring Initializr</a> e gere um novo projeto Spring AI com as seguintes dependências:</p><ul><li><code>Elasticsearch Vector Store</code></li><li><code>Spring Boot Actuator</code></li><li><code>GraalVM</code></li><li><code>OpenAI</code></li><li><code>Web</code></li></ul><p>Certifique-se de escolher a versão mais recente do Java (idealmente Java 24 - na data desta publicação - ou posterior) e a ferramenta de compilação de sua preferência. Neste exemplo, estamos usando o Apache Maven.</p><p>Clique em <code>Generate</code> e depois descompacte o projeto e importe-o para o seu IDE de escolha. (Estamos usando o IntelliJ IDEA.)</p><p>Em primeiro lugar, vamos especificar os detalhes de conexão para sua aplicação Spring Boot. Em <code>application.properties,</code> escreva o seguinte:</p><pre><code>spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=w1GB15uQ</code></pre><p>Também utilizaremos a capacidade de armazenamento vetorial do Spring AI para inicializar o que for necessário no lado do Elasticsearch em termos de estruturas de dados, portanto, especifique:</p><pre><code>spring.ai.vectorstore.elasticsearch.initialize-schema=true</code></pre><p>Nesta demonstração, usaremos <strong>o OpenAI</strong> , especificamente o <strong>Modelo de Incorporação</strong> e <strong>o Modelo de Chat </strong>(sinta-se à vontade para usar o serviço de sua preferência, desde que <a href="https://docs.spring.io/spring-ai/reference/api/embeddings.html#available-implementations">o Spring AI o suporte</a>).</p><p>O modelo de incorporação é necessário para criar representações dos dados antes de armazená-los no Elasticsearch. Para que o OpenAI funcione, precisamos especificar o <code>API key</code>:</p><pre><code>spring.ai.openai.api-key=...</code></pre><p>Você pode defini-la como uma variável de ambiente, como <code>SPRING_AI_OPENAI_API_KEY</code> para evitar armazenar a credencial no seu código-fonte.</p><p>Vamos enviar arquivos, então certifique-se de personalizar a quantidade de dados que pode ser enviada para o contêiner de servlet:</p><pre><code>spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB</code></pre><p>Estamos quase lá! Antes de começarmos a escrever o código, vamos ter uma prévia de como isso vai funcionar.</p><p>Em nossa máquina, baixamos o <a href="https://images-cdn.fantasyflightgames.com/filer_public/9f/aa/9faa23a3-9f71-4c77-865f-bba4aac8a258/runewars-revised-_rulebook.pdf">seguinte arquivo</a> (uma lista de regras para um jogo de tabuleiro), renomeamos para <code>test.pdf</code> e o colocamos em <code>~/Downloads/test.pdf</code>.</p><p>O arquivo será enviado para o endpoint <code>/rag/ingest</code> (substitua o caminho de acordo com sua configuração local):</p><pre><code>http --form POST http://localhost:8080/rag/ingest path@/Users/jlong/Downloads/test.pdf</code></pre><p>Isso pode levar alguns segundos…</p><p>Nos bastidores, os dados são enviados para a OpenAI, que cria representações vetoriais (embeddings) dos dados; esses dados são então gravados no Elasticsearch, tanto os vetores quanto o texto original.</p><p>É nesses dados, juntamente com todos os elementos incorporados neles, que a mágica acontece. Podemos então consultar o Elasticsearch usando a interface <code>VectorStore</code> .</p><p>O fluxo completo se parece com isto:</p><ul><li>O cliente HTTP carrega o PDF de sua escolha para a aplicação Spring.</li><li>O Spring AI se encarrega da extração de texto do nosso PDF e divide cada página em blocos de 800 caracteres.</li><li>A OpenAI gera a representação vetorial para cada segmento.</li><li>Tanto o texto fragmentado quanto o arquivo incorporado são armazenados no Elasticsearch.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/9734adb2d7128e61c515d5855dfad6d3a326a4a1-1454x706.png" style="width: 100%;" /><p>Por fim, vamos fazer uma consulta:</p><pre><code>http :8080/rag/query question=="where do you place the reward card after obtaining it?" </code></pre><p>E obteremos uma resposta relevante:</p><pre><code>After obtaining a Reward card, you place it facedown under the Hero card of the hero who received it.
Found at page: 28 of the manual</code></pre><p>Que legal! Como tudo isso funciona?</p><ul><li>O cliente HTTP envia a pergunta para a aplicação Spring.</li><li>O Spring AI obtém a representação vetorial da pergunta da OpenAI.</li><li>Com essa incorporação, ele busca documentos semelhantes nos blocos armazenados do Elasticsearch e recupera os documentos mais similares.</li><li>A Spring AI envia então a pergunta e o contexto obtido para a OpenAI para gerar uma resposta LLM.</li><li>Por fim, retorna a resposta gerada e uma referência ao contexto recuperado.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3799de6e8cb13ce49b9e136cfe593263030231a8-1464x1050.png" style="width: 100%;" /><p>Vamos analisar o código Java para ver como ele realmente funciona.</p><p>Primeiramente, a classe <strong>Main</strong> : é uma classe principal padrão para qualquer aplicação Spring Boot.</p><pre><code>@SpringBootApplication
public class DemoApplication {
 	public static void main(String[] args) { 
     		SpringApplication.run(DemoApplication.class, args);
 	}
}</code></pre><p>Nada para ver ali. Continuando…</p><p>Em seguida, um controlador HTTP básico:</p><pre><code>@RestController
class RagController {

   private final RagService rag;

   RagController(RagService rag) {
       this.rag = rag;
   }

   @PostMapping("/rag/ingest")
   ResponseEntity<?> ingestPDF(@RequestBody MultipartFile path) {
       rag.ingest(path.getResource());
       return ResponseEntity.ok().body("Done!");
   }

   @GetMapping("/rag/query")
   ResponseEntity<?> query(@RequestParam String question) {
       String response = rag.directRag(question);
       return ResponseEntity.ok().body(response);
   }
}</code></pre><p>O controlador está simplesmente chamando um serviço que criamos para lidar com a ingestão de arquivos e sua gravação no armazenamento vetorial do Elasticsearch, e então facilitar consultas nesse mesmo armazenamento vetorial.</p><p>Vamos analisar o serviço:</p><pre><code>@Service
class RagService {

   private final ElasticsearchVectorStore vectorStore;

   private final ChatClient ai;

   RagService(ElasticsearchVectorStore vectorStore, ChatClient.Builder clientBuilder) {
       this.vectorStore = vectorStore;
       this.ai = clientBuilder.build();
   }

   void ingest(Resource path) {
       PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(path);
       List<Document> batch = new TokenTextSplitter().apply(pdfReader.read());
       vectorStore.add(batch);
   }

  // TBD
}</code></pre><p>Este código lida com toda a ingestão: dado um <code>Resource</code> do Spring Framework, que é um contêiner em torno de bytes, lemos os dados do PDF (presumido ser um arquivo <code>.PDF</code> - certifique-se de validar isso antes de aceitar entradas arbitrárias!) usando o <code>PagePdfDocumentReader</code> do Spring AI e, em seguida, tokenizamos usando o <code>TokenTextSplitter</code> do Spring AI, finalmente adicionando os <code>List&lt;Document&gt;</code>resultantes à implementação do <code>VectorStore</code> , <code>ElasticsearchVectorStore</code>.</p><p>Você pode confirmar isso usando o Kibana: depois de enviar um arquivo para o endpoint <code>/rag/ingest</code> , abra seu navegador em <code>localhost:5601</code> e no menu lateral à esquerda navegue até <code>Dev Tools</code>. Ali você pode enviar consultas para interagir com os dados na instância do Elasticsearch.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/c85e522f02f8b2da7462cd428dc7e952c9692542-1600x1040.png" style="width: 100%;" /><p>Faça uma consulta como esta:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/00974a176cbce11e70fcab24fb4b3f9c6e205982-1600x1040.png" style="width: 100%;" /><p>Agora vem a parte divertida: como recuperamos esses dados em resposta às consultas dos usuários?</p><p>Aqui está uma primeira tentativa de implementação da consulta, em um método chamado <code>directRag</code>.</p><pre><code>String directRag(String question) {
   // Query the vector store for documents related to the question
   List<Document> vectorStoreResult =
           vectorStore.doSimilaritySearch(SearchRequest.builder().query(question).topK(5)
                   .similarityThreshold(0.7).build());

   // Merging the documents into a single string
   String documents = vectorStoreResult.stream()
           .map(Document::getText)
           .collect(Collectors.joining(System.lineSeparator()));

   // Exit if the vector search didn't find any results
   if (documents.isEmpty()) {
       return "No relevant context found. Please change your question.";
   }

   // Setting the prompt with the context
   String prompt = """
           You're assisting with providing the rules of the tabletop game Runewars.
           Use the information from the DOCUMENTS section to provide accurate answers to the
           question in the QUESTION section.
           If unsure, simply state that you don't know.
          
           DOCUMENTS:
           """ + documents
           + """
           QUESTION:
           """ + question;


   // Calling the chat model with the question
   String response = ai
           .prompt()
           .user(prompt)
           .call()
           .content();

   return response +
           System.lineSeparator() +
           "Found at page: " +
           // Retrieving the first ranked page number from the document metadata
           vectorStoreResult.getFirst().getMetadata().get(PagePdfDocumentReader.METADATA_START_PAGE_NUMBER) +
           " of the manual";

}</code></pre><p>O código é bastante simples, mas vamos dividi-lo em várias etapas:</p><ol><li>Use <code>VectorStore</code> para realizar uma pesquisa de similaridade.</li><li>Dados todos os resultados, obtenha os <code>Document</code>subjacentes do Spring AI e extraia seu texto, concatenando-os em um único resultado.</li><li>Envie os resultados de <code>VectorStore</code> para o modelo, juntamente com uma instrução para o modelo saber o que fazer com eles e a pergunta do usuário. Aguarde a resposta e retorne-a.</li></ol><p></p><p>Isto é <strong>RAG</strong> - geração aumentada de recuperação. A ideia é usar dados de um repositório vetorial para orientar o processamento e a análise realizados pelo modelo. Agora que você já sabe como fazer, esperemos que nunca precise fazer! Não assim: <a href="https://docs.spring.io/spring-ai/reference/api/advisors.html">os Advisors</a> da Spring AI estão aqui para simplificar ainda mais esse processo.</p><p>O Advisors permite pré-processar e pós-processar uma solicitação para um determinado modelo, além de fornecer uma camada de abstração entre seu aplicativo e o armazenamento de vetores. Adicione a seguinte dependência à sua compilação:<br/></p><pre><code><dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency></code></pre><p>Adicione outro método chamado <code>advisedRag(String question)</code> à classe:</p><pre><code>String advisedRag(String question) {
   return this.ai
           .prompt()
           .user(question)
           .advisors(new QuestionAnswerAdvisor(vectorStore))
           .call()
           .content();
}</code></pre><p>Toda a lógica do padrão RAG está encapsulada em <code>QuestionAnswerAdvisor</code>. Todo o resto é exatamente como qualquer solicitação a um <code>ChatModel</code> seria! Legal!</p><p>E você pode <a href="https://github.com/xeraa/rag-with-java-springai-elasticsearch">obter o código completo no GitHub</a>.</p><h2>Conclusão</h2><p>Nesta demonstração, usamos imagens Docker e fizemos tudo em nossa máquina local, mas o objetivo aqui é construir sistemas e serviços de IA prontos para produção. Existem várias coisas que você pode fazer para tornar isso realidade.</p><p>Em primeiro lugar, você pode adicionar <a href="https://docs.spring.io/spring-boot/reference/actuator/index.html#actuator">o Spring Boot Actuator</a> para monitorar o consumo de tokens. Os tokens são uma representação da complexidade (e, às vezes, do custo em dólares) de uma determinada solicitação ao modelo.</p><p>Você já tem o Spring Boot Actuator no classpath, então basta especificar as seguintes propriedades para exibir todas as métricas (capturadas pelo magnífico projeto <a href="http://micrometer.io">Micrometer.io</a> ):</p><pre><code>management.endpoints.web.exposure.include=*</code></pre><p>Reinicie o aplicativo. Faça uma consulta e depois acesse: <a href="http://localhost:8080/actuator/metrics">http://localhost:8080/actuator/metrics</a>. Pesquise por “<code>token</code>” e você verá informações sobre os tokens que estão sendo usados pelo aplicativo. Fique de olho nisso. Você também pode usar <a href="https://docs.micrometer.io/micrometer/reference/implementations/elastic.html">a integração do Micrometer com o Elasticsearch</a> para enviar essas métricas e fazer com que o Elasticsearch funcione como seu banco de dados de séries temporais preferido!</p><p>Você deve então considerar que, cada vez que fazemos uma solicitação a um armazenamento de dados como o Elasticsearch, ou ao OpenAI, ou a outros serviços de rede, estamos realizando operações de entrada/saída (E/S) e, frequentemente, essas operações de E/S bloqueiam os threads nos quais são executadas. O Java 21 e versões posteriores incluem <strong>threads virtuais</strong> não bloqueantes que melhoram drasticamente a escalabilidade. Ative-o com:<br/></p><pre><code>spring.threads.virtual.enabled=true</code></pre><p>E, por fim, você vai querer hospedar seu aplicativo e seus dados em um local onde eles possam prosperar e ser escaláveis. Temos certeza de que você provavelmente já pensou em onde executar seu aplicativo, mas onde você hospedará seus dados? Podemos recomendar a <a href="https://cloud.elastic.co/">Elastic Cloud</a>? É seguro, privado, escalável e repleto de recursos. Nossa parte favorita? Se quiser, você pode adquirir a edição Serverless, onde o Elasticsearch é que controla o pager, e não você!</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch em JavaScript da maneira correta, parte II]]></title>
            <description><![CDATA[Conheça as práticas recomendadas de produção e como executar o cliente Elasticsearch Node.js em ambientes serverless para reduzir erros na codificação. ]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/how-to-use-elasticsearch-in-javascript-part-ii</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/how-to-use-elasticsearch-in-javascript-part-ii</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Esta é a segunda parte da nossa série sobre Elasticsearch em JavaScript. Na<a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i"> primeira parte,</a> aprendemos como configurar nosso ambiente corretamente, configurar o cliente Node.js, indexar dados e realizar buscas. Nesta segunda parte, aprenderemos como implementar as melhores práticas de produção e executar o cliente Elasticsearch <a href="http://node.js">Node.js</a> em ambientes Serverless.</p><p>Analisaremos:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#production-best-practices">Melhores práticas de produção</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#error-handling">Tratamento de erros</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#testing">Teste</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#serverless-environments">Ambientes sem servidor</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#running-the-client-on-elastic-serverless">Executando o cliente no Elastic Serverless</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-ii#running-the-client-on-function-as-a-service-environment">Executando o cliente em um ambiente de função como serviço.</a></li></ul></li></ul><p><em>Você pode conferir o código-fonte com os exemplos <strong><a href="https://github.com/Delacrobix/JS-client-best-practices_article">aqui</a>.</strong></em></p><h2>Melhores práticas de produção</h2><h3>Tratamento de erros no Elasticsearch</h3><p>Uma funcionalidade útil do cliente Elasticsearch em Node.js é que ele expõe objetos para os possíveis erros no Elasticsearch, permitindo que você os valide e trate de diferentes maneiras.</p><p>Para <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/connecting#client-error-handling">ver todos</a>, execute o seguinte comando: </p><pre><code>const { errors } = require('@elastic/elasticsearch')
console.log(errors)</code></pre><p>Vamos voltar ao exemplo de pesquisa e tratar de alguns dos possíveis erros:</p><pre><code>app.get("/search/lexic", async (req, res) => {
 ....
  } catch (error) {
    if (error instanceof errors.ResponseError) {
      let errorMessage =
        "Response error!, query malformed or server down, contact the administrator!";

      if (error.body.error.type === "parsing_exception") {
        errorMessage = "Query malformed, make sure mappings are set correctly";
      }

      res.status(error.meta.statusCode).json({
        erroStatus: error.meta.statusCode,
        success: false,
        results: null,
        error: errorMessage,
      });
    }

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p><code>ResponseError</code> em particular, ocorrerá quando a resposta for <code>4xx</code> ou <code>5xx</code>, o que significa que a solicitação está incorreta ou o servidor não está disponível.</p><p>Podemos testar esse tipo de erro gerando consultas incorretas, como tentar <strong>fazer uma consulta de termo em um campo do tipo texto:</strong></p><p>Erro padrão:</p><pre><code> {
    "success": false,
    "results": null,
    "error": "parsing_exception\n\tRoot causes:\n\t\tparsing_exception: [terms] query does not support [visit_details]"
}</code></pre><p>Erro personalizado: </p><pre><code>{
    "erroStatus": 400,
    "success": false,
    "results": null,
    "error": "Response error!, query malformed or server down; contact the administrator!"
}</code></pre><p>Também podemos capturar e lidar com cada tipo de erro de uma determinada maneira. Por exemplo, podemos adicionar lógica de repetição em um <code>TimeoutError</code>.</p><pre><code>app.get("/search/semantic", async (req, res) => {
    try {
  ...
  } catch (error) {
    if (error instanceof errors.TimeoutError) {


     // Retry logic...

      res.status(error.meta.statusCode).json({
        erroStatus: error.meta.statusCode,
        success: false,
        results: null,
        error:
          "The request took more than 10s after 3 retries. Try again later.",
      });
    }
  }
});</code></pre><h3>Teste</h3><p>Os testes são essenciais para garantir a estabilidade do aplicativo. Para testar o código de forma isolada do Elasticsearch, podemos usar a biblioteca <a href="https://github.com/elastic/elasticsearch-js-mock">elasticsearch-js-mock</a> ao criar nosso cluster.</p><p>Esta biblioteca permite instanciar um cliente muito semelhante ao real, mas que responderá à nossa configuração substituindo apenas a camada HTTP do cliente por uma camada simulada, mantendo o restante igual ao original.</p><p>Vamos instalar a biblioteca mocks e <a href="https://github.com/avajs/ava">o AVA</a> para testes automatizados.</p><p><code>npm install @elastic/elasticsearch-mock</code></p><p><code>npm install --save-dev ava</code></p><p>Vamos configurar o arquivo <code>package.json</code> para executar os testes. Certifique-se de que esteja assim:</p><pre><code>"type": "module",
	"scripts": {
		"test": "ava"
	},
	"devDependencies": {
		"ava": "^5.0.0"
	}</code></pre><p>Vamos agora criar um arquivo <code>test.js</code> e instalar nosso cliente de simulação:</p><pre><code>const { Client } = require('@elastic/elasticsearch')
const Mock = require('@elastic/elasticsearch-mock')

const mock = new Mock()
const client = new Client({
  node: 'http://localhost:9200',
  Connection: mock.getConnection()
})</code></pre><p>Agora, adicione uma simulação para pesquisa semântica:</p><pre><code>function createSemanticSearchMock(query, indexName) {
  mock.add(
    {
      method: "POST",
      path: `/${indexName}/_search`,
      body: {
        query: {
          semantic: {
            field: "semantic_field",
            query: query,
          },
        },
      },
    },
    () => {
      return {
        hits: {
          total: { value: 2, relation: "eq" },
          hits: [
            {
              _id: "1",
              _score: 0.9,
              _source: {
                owner_name: "Alice Johnson",
                pet_name: "Buddy",
                species: "Dog",
                breed: "Golden Retriever",
                vaccination_history: ["Rabies", "Parvovirus", "Distemper"],
                visit_details:
                  "Annual check-up and nail trimming. Healthy and active.",
              },
            },
            {
              _id: "2",
              _score: 0.7,
              _source: {
                owner_name: "Daniel Kim",
                pet_name: "Mochi",
                species: "Rabbit",
                breed: "Mixed",
                vaccination_history: [],
                visit_details:
                  "Nail trimming and general health check. No issues.",
              },
            },
          ],
        },
      };
    }
  );
}</code></pre><p>Agora podemos criar um teste para o nosso código, garantindo que a parte do Elasticsearch sempre retorne os mesmos resultados:</p><pre><code>import test from 'ava';

test("performSemanticSearch must return formatted results correctly", async (t) => {
  const indexName = "vet-visits";
  const query = "Which pets had nail trimming?";

  createSemanticSearchMock(query, indexName);

  async function performSemanticSearch(esClient, q, indexName = "vet-visits") {
    try {
      const result = await esClient.search({
        index: indexName,
        body: {
          query: {
            semantic: {
              field: "semantic_field",
              query: q,
            },
          },
        },
      });

      return {
        success: true,
        results: result.hits.hits,
      };
    } catch (error) {
      if (error instanceof errors.TimeoutError) {
        return {
          success: false,
          results: null,
          error: error.body.error.reason,
        };
      }

      return {
        success: false,
        results: null,
        error: error.message,
      };
    }
  }

  const result = await performSemanticSearch(esClient, query, indexName);

  t.true(result.success, "The search must be successful");
  t.true(Array.isArray(result.results), "The results must be an array");

  if (result.results.length > 0) {
    t.true(
      "_source" in result.results[0],
      "Each result must have a _source property"
    );
    t.true(
      "pet_name" in result.results[0]._source,
      "Results must include the pet_name field"
    );
    t.true(
      "visit_details" in result.results[0]._source,
      "Results must include the visit_details field"
    );
  }
});</code></pre><p>Vamos executar os testes.</p><p><code>npm run test</code></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/42feae845ae8eae03c37ad7ad114e8db35984812-1186x302.png" style="width: 100%;" /><p>Pronto! A partir de agora, podemos testar nosso aplicativo focando 100% no código e não em fatores externos.</p><h2>Ambientes sem servidor</h2><h3>Como executar o cliente no Elastic Serverless</h3><p>Já abordamos a execução do Elasticsearch na nuvem ou em infraestrutura local; no entanto, o cliente Node.js também oferece suporte a conexões com o <a href="https://www.elastic.co/guide/en/serverless/current/intro.html">Elastic Cloud Serverless</a>.</p><p>O Elastic Cloud Serverless permite que você crie um projeto onde não precisa se preocupar com a infraestrutura, já que a Elastic cuida disso internamente, e você só precisa se preocupar com os dados que deseja indexar e por quanto tempo deseja ter acesso a eles.</p><p>Do ponto de vista da utilização, o Serverless separa o processamento do armazenamento, proporcionando recursos de escalonamento automático tanto para <a href="https://www.elastic.co/search-labs/blog/elasticsearch-serverless-tier-autoscaling">pesquisa</a> quanto para <a href="https://www.elastic.co/search-labs/blog/elasticsearch-ingest-autoscaling">indexação</a>. Isso permite que você cultive apenas os recursos de que realmente precisa.</p><p>O cliente realiza as seguintes adaptações para se conectar ao Serverless:</p><ul><li>Desativa a detecção de pacotes e ignora quaisquer opções relacionadas a ela.</li><li>Ignora todos os nós passados na configuração, exceto o primeiro, e ignora quaisquer opções de filtragem e seleção de nós.</li><li>Habilita a compressão e o método `TLSv1_2_method` (igual à configuração para o Elastic Cloud).</li><li>Adiciona um cabeçalho HTTP `elastic-api-version` a todas as requisições.</li><li>Utiliza `CloudConnectionPool` por padrão em vez de `WeightedConnectionPool`.</li><li>Desativa os cabeçalhos `content-type` e `accept` fornecidos pelo fornecedor, em favor dos tipos MIME padrão.</li></ul><p>Para conectar seu projeto sem servidor, você precisa usar o parâmetro serverMode: serverless.</p><pre><code>const { Client } = require('@elastic/elasticsearch')
const client = new Client({
  node: 'ELASTICSEARCH_ENDPOINT',
  auth: { apiKey: 'ELASTICSEARCH_API_KEY' },
  serverMode: "serverless",
});</code></pre><h3>Como executar o cliente em um ambiente de função como serviço</h3><p>No exemplo, usamos um servidor Node.js, mas você também pode se conectar usando um ambiente de função como serviço com funções como AWS Lambda, GCP Run, etc.</p><pre><code>'use strict'

const { Client } = require('@elastic/elasticsearch')

const client = new Client({
  // client initialisation
})

exports.handler = async function (event, context) {
  // use the client
}</code></pre><p>Outro exemplo é conectar-se a serviços como o Vercel, que também é serverless. Você pode conferir este <a href="https://github.com/elastic/elasticsearch-js/blob/main/docs/examples/proxy/README.md">exemplo completo</a> de como fazer isso, mas a parte mais relevante do <a href="https://github.com/elastic/elasticsearch-js/blob/main/docs/examples/proxy/api/search.js">endpoint de pesquisa</a> se parece com isto:</p><pre><code>const response = await client.search(
  {
    index: INDEX,
    // You could directly send from the browser
    // the Elasticsearch's query DSL, but it will
    // expose you to the risk that a malicious user
    // could overload your cluster by crafting
    // expensive queries.
    query: {
      match: { field: req.body.text },
    },
  },
  {
    headers: {
      Authorization: `ApiKey ${token}`,
    },
  }
);</code></pre><p>Este endpoint reside na pasta /api e é executado a partir do lado do servidor, de forma que o cliente só tenha controle sobre o parâmetro “texto” que corresponde ao termo de pesquisa.</p><p>A implicação de usar a função como serviço é que, ao contrário de um servidor que funciona 24 horas por dia, 7 dias por semana, as funções apenas ativam a máquina que executa a função e, assim que ela termina, a máquina entra em modo de repouso para consumir menos recursos.</p><p>Essa configuração pode ser conveniente se o aplicativo não receber muitas solicitações; caso contrário, os custos podem ser elevados. Você também precisa levar em consideração o <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html">ciclo de vida das funções</a> e os tempos de execução (que, em alguns casos, podem ser de apenas alguns segundos).</p><h2>Conclusão</h2><p>Neste artigo, aprendemos como lidar com erros, o que é crucial em ambientes de produção. Também abordamos os testes da nossa aplicação enquanto simulávamos o serviço Elasticsearch, o que proporciona testes confiáveis independentemente do estado do cluster e nos permite focar no nosso código.</p><p>Por fim, demonstramos como criar uma infraestrutura totalmente sem servidor, provisionando tanto o Elastic Cloud Serverless quanto um aplicativo Vercel.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como otimizar o espaço em disco e o uso do Elasticsearch]]></title>
            <description><![CDATA[Saiba como evitar e lidar com casos em que o disco do Elasticsearch está muito cheio (uso excessivo) e quando a capacidade do disco é subutilizada para otimizar os custos do cluster.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/optimize-elasticsearch-disk-space-and-usage</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/optimize-elasticsearch-disk-space-and-usage</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 16 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/5d41a078dd03e4480a0ff4e9591c8618b9bab4d0-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O gerenciamento de disco é importante em qualquer banco de dados, e o Elasticsearch não é exceção. Se você não tiver espaço em disco suficiente disponível, o Elasticsearch deixará de alocar shards para o nó. Isso acabará por impedi-lo de gravar dados no cluster, com o risco potencial de perda de dados em sua aplicação. Por outro lado, se você tiver muito espaço em disco, estará pagando por mais recursos do que precisa.</p><h2>Informações básicas sobre marcas d&#x27;água</h2><p>Existem vários limites de &quot;marca d&#x27;água&quot; no seu cluster Elasticsearch que ajudam a monitorar o espaço em disco disponível. À medida que o disco de um nó se enche, o primeiro limite a ser ultrapassado será o &quot;limite mínimo de espaço em disco&quot;. O segundo limite será então o “limite de marca d&#x27;água de disco alto”. Finalmente, será atingida a “fase de inundação do disco”. Assim que esse limite for ultrapassado, o cluster bloqueará a gravação em TODOS os índices que possuam um shard (primário ou réplica) no nó que atingiu o limite. As leituras (buscas) ainda serão possíveis.</p><h2>Como prevenir e lidar com casos em que o disco está muito cheio (sobreutilização)</h2><p>Existem vários métodos para lidar com casos em que o disco do Elasticsearch está muito cheio:</p><ol><li><strong>Excluir</strong> <strong>dados antigos:</strong> Normalmente, os dados não devem ser mantidos indefinidamente. Uma forma de prevenir e resolver o problema de disco cheio é garantir que, quando os dados atingirem uma certa idade, sejam arquivados e excluídos de forma confiável. Uma maneira de fazer isso é usar <a href="https://www.elastic.co/docs/manage-data/lifecycle/index-lifecycle-management">o ILM</a>.</li><li><strong>Adicionar capacidade de armazenamento:</strong> Se não for possível excluir os dados, talvez seja necessário adicionar mais nós de dados ou aumentar o tamanho dos discos para reter todos os dados sem afetar negativamente o desempenho. Se precisar adicionar capacidade de armazenamento ao cluster, considere se precisa adicionar apenas capacidade de armazenamento ou se deve adicionar também recursos de RAM e CPU em proporção adequada (consulte a seção sobre <a href="https://www.elastic.co/search-labs/blog/optimize-elasticsearch-disk-space-and-usage#the-relationship-between-disk-size,-ram-and-cpu">proporção de tamanho do disco, RAM e CPU</a> abaixo).</li></ol><h2>Como adicionar capacidade de armazenamento ao seu cluster Elasticsearch</h2><ol><li><strong>Aumente o número de nós de dados: </strong>Lembre-se de que os novos nós devem ter o mesmo tamanho que os nós existentes e a mesma versão do Elasticsearch.</li><li><strong>Aumentar o tamanho dos nós existentes: </strong>Em ambientes baseados em nuvem, geralmente é fácil aumentar o tamanho do disco e a RAM/CPU nos nós existentes.</li><li><strong>Aumentar apenas o tamanho do disco: </strong>Em ambientes baseados em nuvem, geralmente é relativamente fácil aumentar o tamanho do disco.</li><li><a href="https://www.elastic.co/docs/deploy-manage/tools/snapshot-and-restore"><strong>Instantâneo</strong> <strong>e</strong> <strong>Restauração</strong></a><strong>:</strong> Se você deseja permitir que dados antigos sejam recuperados sob demanda em um processo automatizado a partir de backups, você pode criar snapshots de índices antigos, excluí-los e restaurar os dados temporariamente sob demanda a partir desses snapshots. </li><li><strong>Reduzir o número de réplicas por fragmento:</strong> Outra opção para reduzir os dados é diminuir o número de réplicas de cada fragmento. Para alta disponibilidade, o ideal é ter uma réplica por fragmento, mas quando os dados ficam mais antigos, pode ser possível trabalhar sem réplicas. Isso geralmente funciona se os dados forem persistentes ou se você tiver um backup para restaurar, se necessário.</li><li><strong>Criar alertas:</strong> Para evitar que os discos fiquem cheios no futuro e agir de forma proativa, você deve criar alertas com base no uso do disco que o notificarão quando o disco começar a ficar cheio. </li></ol><h2>Como prevenir e lidar com casos em que a capacidade do disco está subutilizada</h2><p>Se a capacidade do seu disco estiver subutilizada, existem várias opções para reduzir o volume de armazenamento no seu cluster.</p><h3>Como reduzir o volume de armazenamento em um cluster Elasticsearch</h3><p>Existem vários métodos para reduzir o volume de armazenamento de um cluster.</p><p><strong>1. Reduzir o número de nós de dados</strong></p><p>Se você deseja reduzir o armazenamento de dados e também reduzir os recursos de RAM e CPU na mesma proporção, então esta é a estratégia mais fácil. A desativação de nós desnecessários provavelmente proporcionará a maior economia de custos.</p><p>Antes de desativar o nó, você deve:</p><ul><li>Certifique-se de que o nó a ser desativado não seja necessário como nó MESTRE. Você deve sempre ter pelo menos três nós com a função de nó MESTRE.</li><li>Migre os fragmentos de dados para fora do nó a ser desativado.</li></ul><p><strong>2. Substitua os nós existentes por nós menores.</strong></p><p>Se não for possível reduzir ainda mais o número de nós (normalmente, 3 seria uma configuração mínima), então você pode querer diminuir o tamanho dos nós existentes. Lembre-se de que é recomendável garantir que todos os nós de dados tenham a mesma quantidade de memória RAM e tamanho de disco, já que o balanceamento dos shards é feito com base no número de shards por nó.</p><p>O processo seria o seguinte:</p><ul><li>Adicione novos nós menores ao cluster.</li><li>Migre os fragmentos para longe dos nós que serão desativados.</li><li>Desligue os nós antigos.</li></ul><p><strong>3. Reduzir o tamanho do disco nos nós</strong></p><p>Se você deseja reduzir APENAS o tamanho do disco nos nós, sem alterar a RAM ou a CPU geral do cluster, então você pode reduzir o tamanho do disco para cada nó individualmente. Reduzir o tamanho do disco em um nó do Elasticsearch não é um processo trivial.</p><p>A maneira mais fácil de fazer isso geralmente seria:</p><ul><li>Migrar fragmentos do nó</li><li>Pare o nó</li><li>Monte um novo volume de dados no nó com o tamanho apropriado.</li><li>Copie todos os dados do volume de disco antigo para o novo volume.</li><li>Desprenda o volume antigo A.</li><li>Inicie o nó e migre os fragmentos de volta para o nó.</li></ul><p>Isso exige que você tenha capacidade suficiente nos outros nós para armazenar temporariamente os fragmentos extras do nó durante esse processo. Em muitos casos, o custo de gerenciamento desse processo pode exceder a economia potencial no uso de disco. Por esse motivo, pode ser mais simples substituir o nó por completo por um novo nó com o tamanho de disco desejado (consulte “Substituir nós existentes por nós menores” acima).</p><p>Ao pagar por recursos desnecessários, os custos podem ser reduzidos otimizando a utilização desses recursos.</p><h2>A relação entre o tamanho do disco, a RAM e a CPU.</h2><p>A proporção ideal entre capacidade de disco e RAM no seu cluster dependerá do seu caso de uso específico. Por esse motivo, ao considerar alterações na sua capacidade de armazenamento, você também deve avaliar se as proporções atuais de disco/RAM/CPU estão adequadamente equilibradas e se, consequentemente, você precisa adicionar/reduzir RAM/CPU na mesma proporção.</p><p>Os requisitos de RAM e CPU dependem do volume de atividade <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-indexing/">de indexação</a> , do número e tipo de consultas, bem como da quantidade de dados que está sendo pesquisada e agregada. Isso geralmente é proporcional à quantidade de dados armazenados no cluster e, portanto, também deve estar relacionado ao tamanho do disco.</p><p>A proporção entre a capacidade do disco e a RAM pode variar dependendo do uso. Veja alguns exemplos aqui:</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;"></td><td style="border: 1px solid #ccc; padding: 4px;">Atividade do índice</td><td style="border: 1px solid #ccc; padding: 4px;">Retenção</td><td style="border: 1px solid #ccc; padding: 4px;">Atividade de pesquisa</td><td style="border: 1px solid #ccc; padding: 4px;">Capacidade do disco</td><td style="border: 1px solid #ccc; padding: 4px;">BATER</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Aplicativo de busca empresarial</td><td style="border: 1px solid #ccc; padding: 4px;">Ingestão moderada de toras</td><td style="border: 1px solid #ccc; padding: 4px;">Longo</td><td style="border: 1px solid #ccc; padding: 4px;">Luz</td><td style="border: 1px solid #ccc; padding: 4px;">2TB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Monitoramento de aplicativos</td><td style="border: 1px solid #ccc; padding: 4px;">Ingestão intensiva de toras</td><td style="border: 1px solid #ccc; padding: 4px;">Curto</td><td style="border: 1px solid #ccc; padding: 4px;">Luz</td><td style="border: 1px solid #ccc; padding: 4px;">1TB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Comércio eletrônico</td><td style="border: 1px solid #ccc; padding: 4px;">Indexação de dados leves</td><td style="border: 1px solid #ccc; padding: 4px;">Indeterminado</td><td style="border: 1px solid #ccc; padding: 4px;">Pesado</td><td style="border: 1px solid #ccc; padding: 4px;">500 GB</td><td style="border: 1px solid #ccc; padding: 4px;">32 GB</td>
                    </tr>
              </tbody>
            </table>
          <p><em>Lembre-se de que modificar a configuração das máquinas de nó deve ser feito com cuidado, pois pode causar indisponibilidade do nó e você precisa garantir que os shards não comecem a migrar para seus outros nós já sobrecarregados.</em></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Elasticsearch em JavaScript da maneira correta, parte I]]></title>
            <description><![CDATA[Explicando como criar um backend Elasticsearch pronto para produção em JavaScript.  

Saiba como usar o Elasticsearch com JavaScript para criar um servidor com diferentes endpoints de busca para consultar documentos do Elasticsearch, seguindo as melhores práticas de cliente/servidor.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/how-to-use-elasticsearch-in-javascript-part-i</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/how-to-use-elasticsearch-in-javascript-part-i</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
            <pubDate>Thu, 15 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/72200540ed258779d87e53a72ea189f8a138540c-1600x901.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Este é o primeiro artigo de uma série que aborda como usar o Elasticsearch com JavaScript. Nesta série, você aprenderá o básico de como usar o Elasticsearch em um ambiente JavaScript e revisará os recursos mais relevantes e as melhores práticas para criar um aplicativo de busca. Ao final, você saberá tudo o que precisa para executar o Elasticsearch usando JavaScript.</p><p>Nesta primeira parte, vamos analisar:</p><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#environment">Ambiente</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#frontend,-backend,-or-serverless?">Frontend, backend ou serverless?</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#connecting-the-client">Conectando o cliente</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#indexing-documents">Documentos de indexação</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#elasticsearch-client">Cliente Elasticsearch</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#semantic-mappings">Mapeamentos semânticos</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#bulk-helper">Auxiliar em massa</a></li></ul></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#searching-data">Dados de pesquisa</a><ul><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#lexical-query-(/search/lexic?q=%3Cquery-term%3E)">Consulta Lexical</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#semantic-query-(/search/semantic?q=%3Cquery-term%3E)">Consulta semântica</a></li><li><a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i#hybrid-query-(/search/hybrid?q=%3Cquery-term%3E)">Consulta híbrida</a></li></ul></li></ul><p><em>Você pode conferir o código-fonte com os exemplos <strong><a href="https://github.com/Delacrobix/JS-client-best-practices_article">aqui</a>.</strong></em></p><h3>O que é o cliente Elasticsearch para Node.js?</h3><p>O <a href="https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html">cliente Elasticsearch para Node.js</a> é uma biblioteca JavaScript que converte as chamadas HTTP REST da API do Elasticsearch em código JavaScript. Isso facilita o manuseio e permite o uso de ferramentas auxiliares que simplificam tarefas como a indexação de documentos em lotes.</p><h2>Ambiente</h2><h3>Frontend, backend ou serverless?</h3><p>Para criar nosso aplicativo de busca usando o cliente JavaScript, precisamos de pelo menos dois componentes: um cluster Elasticsearch e um ambiente de execução JavaScript para executar o cliente.</p><p>O cliente JavaScript é compatível com todas as soluções Elasticsearch (Cloud, on-premise e Serverless), e não há grandes diferenças entre elas, já que o cliente lida com todas as variações internamente, então você não precisa se preocupar com qual usar.</p><p>O ambiente de execução JavaScript, no entanto, deve ser executado a partir do <strong>servidor</strong> e <strong>não diretamente do navegador.</strong></p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/92ce6cfd923c8008fa44f617a58193642d9d5879-661x410.png" style="width: 100%;" /><p>Isso ocorre porque, ao acessar o Elasticsearch pelo navegador, o usuário pode obter informações confidenciais, como a chave da API do cluster, o host ou a própria consulta. A Elasticsearch recomenda <strong>nunca expor o cluster diretamente à internet </strong>e usar uma camada intermediária que abstraia todas essas informações, de forma que o usuário possa ver apenas os parâmetros. Você pode ler mais sobre este tópico <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/es-security-principles.html#security-protect-cluster-traffic">aqui</a>.</p><p>Sugerimos usar um esquema como este:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a08769f08ec73fe57bf2e961cfdfbb1cdd57919d-972x429.png" style="width: 100%;" /><p>Nesse caso, o cliente envia apenas os termos de pesquisa e uma chave de autenticação para o seu servidor, enquanto o seu servidor mantém o controle total da consulta e da comunicação com o Elasticsearch.</p><h3>Conectando o cliente</h3><p>Comece criando uma chave de API seguindo <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">estes passos</a>.</p><p>Seguindo o exemplo anterior, criaremos um servidor Express simples e nos conectaremos a ele usando um cliente de um servidor Node.js.</p><p>Vamos inicializar o projeto com o NPM e instalar o cliente Elasticsearch e <a href="https://expressjs.com/">o Express.</a> Esta última é uma biblioteca para iniciar servidores em Node.js. Usando o Express, podemos interagir com nosso backend via HTTP.</p><p>Vamos inicializar o projeto:</p><p><code>npm init -y</code></p><p>Instalar dependências:</p><p><code>npm install @elastic/elasticsearch express split2 dotenv</code></p><p>Deixe-me explicar melhor:</p><ul><li><a href="https://www.npmjs.com/package/@elastic/elasticsearch"><strong><em>@elastic/elasticsearch</em></strong></a>: É o cliente oficial do Node.js.</li><li><a href="https://www.npmjs.com/package/express"><strong><em>Express</em></strong></a>: Isso nos permitirá criar um servidor Node.js leve para expor o Elasticsearch.</li><li><a href="https://www.npmjs.com/package/split2"><strong><em>split2</em></strong></a>: Divide linhas de texto em um fluxo. Útil para processar nossos arquivos ndjson linha por linha.</li><li><a href="https://www.npmjs.com/package/dotenv"><strong><em>dotenv</em></strong></a>: Permite gerenciar variáveis de ambiente usando um arquivo .env. arquivo</li></ul><p>Crie um arquivo .env Abra o arquivo na raiz do projeto e adicione as seguintes linhas:</p><pre><code>ELASTICSEARCH_ENDPOINT="Your Elasticsearch endpoint"
ELASTICSEARCH_API_KEY="Your Elasticssearch API"</code></pre><p>Dessa forma, podemos importar essas variáveis usando o pacote <code>dotenv</code> .</p><p>Crie um arquivo <code>server.js</code> :</p><pre><code>const express = require("express");
const bodyParser = require("body-parser");
const { Client } = require("@elastic/elasticsearch");
 
require("dotenv").config(); //environment variables setup

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;
const PORT = 3000;


const app = express();

app.listen(PORT, () => {
  console.log("Server running on port", PORT);
});
app.use(bodyParser.json());


let esClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { apiKey: ELASTICSEARCH_API_KEY },  
});

app.get("/ping", async (req, res) => {
  try {
    const result = await esClient.info();

    res.status(200).json({
      success: true,
      clusterInfo: result,
    });
  } catch (error) {
    console.error("Error getting Elasticsearch info:", error);

    res.status(500).json({
      success: false,
      clusterInfo: null,
      error: error.message,
    });
  }
});</code></pre><p>Este código configura um servidor Express.js básico que escuta na porta 3000 e se conecta a um cluster Elasticsearch usando uma chave de API para autenticação. Inclui um endpoint /ping que, quando acessado por meio de uma solicitação GET, consulta o cluster Elasticsearch para obter informações básicas usando o método <code>.info()</code> do cliente Elasticsearch. </p><p>Se a consulta for bem-sucedida, ela retorna as informações do cluster em formato JSON; caso contrário, retorna uma mensagem de erro. O servidor também utiliza o middleware body-parser para lidar com os corpos das requisições JSON.</p><p>Execute o arquivo para iniciar o servidor:</p><p><code>node server.js</code></p><p>A resposta deve ser semelhante a esta:</p><pre><code>Server running on port 3000</code></pre><p>E agora, vamos consultar o endpoint <code>/ping</code> para verificar o status do nosso cluster Elasticsearch.</p><pre><code>curl http://localhost:3000/ping
{
    "success": true,
    "clusterInfo": {
        "name": "instance-0000000000",
        "cluster_name": "61b7e19eec204d59855f5e019acd2689",
        "cluster_uuid": "BIfvfLM0RJWRK_bDCY5ldg",
        "version": {
            "number": "9.0.0",
            "build_flavor": "default",
            "build_type": "docker",
            "build_hash": "112859b85d50de2a7e63f73c8fc70b99eea24291",
            "build_date": "2025-04-08T15:13:46.049795831Z",
            "build_snapshot": false,
            "lucene_version": "10.1.0",
            "minimum_wire_compatibility_version": "8.18.0",
            "minimum_index_compatibility_version": "8.0.0"
        },
        "tagline": "You Know, for Search"
    }
}</code></pre><h2>Documentos de indexação</h2><p>Uma vez conectados, podemos indexar documentos usando mapeamentos como <a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">semantic_text</a> para pesquisa semântica e text para consultas de texto completo. Com esses dois tipos de campo, também podemos fazer <a href="https://www.elastic.co/what-is/hybrid-search">buscas híbridas</a>.</p><p>Criaremos um novo arquivo <code>load.js</code> para gerar os mapeamentos e carregar os documentos.</p><h3>Cliente Elasticsearch</h3><p>Primeiro precisamos instanciar e autenticar o cliente:</p><pre><code>const { Client } = require("@elastic/elasticsearch");

const ELASTICSEARCH_ENDPOINT = "cluster/project_endpoint";
const ELASTICSEARCH_API_KEY = "apiKey";

const esClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { apiKey: ELASTICSEARCH_API_KEY },
});</code></pre><h3>Mapeamentos semânticos</h3><p>Criaremos um índice com dados sobre um hospital veterinário. Armazenaremos as informações do dono, do animal de estimação e os detalhes da visita.</p><p>Os dados nos quais desejamos realizar uma busca de texto completo, como nomes e descrições, serão armazenados como texto. Os dados das categorias, como a espécie ou raça do animal, serão armazenados como palavras-chave.</p><p>Além disso, copiaremos os valores de todos os campos para um campo semantic_text para podermos executar também uma pesquisa semântica nessas informações.</p><pre><code>const INDEX_NAME = "vet-visits";

const createMappings = async (indexName, mapping) => {
  try {
    const body = await esClient.indices.create({
      index: indexName,
      body: {
        mappings: mapping,
      },
    });

    console.log("Index created successfully:", body);
  } catch (error) {
    console.error("Error creating mapping:", error);
  }
};

await createMappings(INDEX_NAME, {
  properties: {
    owner_name: {
      type: "text",
      copy_to: "semantic_field",
    },
    pet_name: {
      type: "text",
      copy_to: "semantic_field",
    },
    species: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    breed: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    vaccination_history: {
      type: "keyword",
      copy_to: "semantic_field",
    },
    visit_details: {
      type: "text",
      copy_to: "semantic_field",
    },
    semantic_field: {
      type: "semantic_text",
    },
  },
});</code></pre><h3>Auxiliar em massa</h3><p>Outra vantagem do cliente é que podemos usar a <a href="https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/client-helpers.html#bulk-helper">função auxiliar</a> de indexação em lotes. A função auxiliar de processamento em lote nos permite lidar facilmente com aspectos como concorrência, novas tentativas e o que fazer com cada documento que passa pela função, seja com sucesso ou com falha.</p><p>Uma característica interessante dessa ferramenta auxiliar é a possibilidade de trabalhar com fluxos de dados. Essa função permite enviar um arquivo linha por linha, em vez de armazenar o arquivo inteiro na memória e enviá-lo para o Elasticsearch de uma só vez.</p><p>Para enviar os dados para o Elasticsearch, crie um arquivo chamado data.ndjson na raiz do projeto e adicione as informações abaixo (alternativamente, você pode baixar o arquivo com o conjunto de dados <a href="https://github.com/Delacrobix/JS-client-best-practices_article/blob/main/data.ndjson">aqui</a>):</p><pre><code>{"owner_name":"Alice Johnson","pet_name":"Buddy","species":"Dog","breed":"Golden Retriever","vaccination_history":["Rabies","Parvovirus","Distemper"],"visit_details":"Annual check-up and nail trimming. Healthy and active."}
{"owner_name":"Marco Rivera","pet_name":"Milo","species":"Cat","breed":"Siamese","vaccination_history":["Rabies","Feline Leukemia"],"visit_details":"Slight eye irritation, prescribed eye drops."}
{"owner_name":"Sandra Lee","pet_name":"Pickles","species":"Guinea Pig","breed":"Mixed","vaccination_history":[],"visit_details":"Loss of appetite, recommended dietary changes."}
{"owner_name":"Jake Thompson","pet_name":"Luna","species":"Dog","breed":"Labrador Mix","vaccination_history":["Rabies","Bordetella"],"visit_details":"Mild ear infection, cleaning and antibiotics given."}
{"owner_name":"Emily Chen","pet_name":"Ziggy","species":"Cat","breed":"Mixed","vaccination_history":["Rabies","Feline Calicivirus"],"visit_details":"Vaccination update and routine physical."}
{"owner_name":"Tomás Herrera","pet_name":"Rex","species":"Dog","breed":"German Shepherd","vaccination_history":["Rabies","Parvovirus","Leptospirosis"],"visit_details":"Follow-up for previous leg strain, improving well."}
{"owner_name":"Nina Park","pet_name":"Coco","species":"Ferret","breed":"Mixed","vaccination_history":["Rabies"],"visit_details":"Slight weight loss; advised new diet."}
{"owner_name":"Leo Martínez","pet_name":"Simba","species":"Cat","breed":"Maine Coon","vaccination_history":["Rabies","Feline Panleukopenia"],"visit_details":"Dental cleaning. Minor tartar buildup removed."}
{"owner_name":"Rachel Green","pet_name":"Rocky","species":"Dog","breed":"Bulldog Mix","vaccination_history":["Rabies","Parvovirus"],"visit_details":"Skin rash, antihistamines prescribed."}
{"owner_name":"Daniel Kim","pet_name":"Mochi","species":"Rabbit","breed":"Mixed","vaccination_history":[],"visit_details":"Nail trimming and general health check. No issues."}</code></pre><p>Usamos o split2 para transmitir as linhas do arquivo enquanto o auxiliar de processamento em lote as envia para o Elasticsearch.</p><pre><code>const { createReadStream } = require("fs");
const split = require("split2");
 
const indexData = async (filePath, indexName) => {
  try {
    console.log(`Indexing data from ${filePath} into ${indexName}...`);

    const result = await esClient.helpers.bulk({
      datasource: createReadStream(filePath).pipe(split()),

      onDocument: () => {
        return {
          index: { _index: indexName },
        };
      },
      onDrop(doc) {
        console.error("Error processing document:", doc);
      },
    });

    console.log("Bulk indexing successful elements:", result.items.length);
  } catch (error) {
    console.error("Error indexing data:", error);
    throw error;
  }
};

await indexData("./data.ndjson", INDEX_NAME);</code></pre><p>O código acima lê um arquivo .ndjson. indexa cada objeto JSON em um índice Elasticsearch especificado usando o método <code>helpers.bulk</code> . Ele transmite o arquivo usando <code>createReadStream</code> e <code>split2</code>, configura metadados de indexação para cada documento e registra quaisquer documentos que não puderem ser processados. Após a conclusão, registra o número de itens indexados com sucesso.</p><p>Alternativamente à função <code>indexData</code> , você pode fazer o upload do arquivo diretamente pela interface do usuário usando o Kibana e usar a <a href="https://www.elastic.co/docs/manage-data/ingest/upload-data-files">interface de upload de arquivos de dados.</a></p><p>Executamos o arquivo para enviar os documentos para o nosso cluster Elasticsearch.</p><p><code>node load.js</code></p><pre><code>Creating mappings for index vet-visits...
Index created successfully: { acknowledged: true, shards_acknowledged: true, index: 'vet-visits' }
Indexing data from ./data.ndjson into vet-visits...
Bulk indexing completed. Total documents: 10, Failed: 0</code></pre><h2>Buscando dados no Elasticsearch</h2><p>Voltando ao nosso arquivo <code>server.js</code> , criaremos diferentes endpoints para realizar buscas lexicais, semânticas ou híbridas.</p><p>Em resumo, esses tipos de pesquisa não são mutuamente exclusivos, mas dependerão do tipo de pergunta que você precisa responder.</p>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Tipo de consulta</td><td style="border: 1px solid #ccc; padding: 4px;">Caso de uso</td><td style="border: 1px solid #ccc; padding: 4px;">Exemplo de pergunta</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consulta lexical</td><td style="border: 1px solid #ccc; padding: 4px;">As palavras ou radicais presentes na pergunta provavelmente aparecerão nos documentos indexados. Similaridade entre tokens na pergunta e nos documentos.</td><td style="border: 1px solid #ccc; padding: 4px;">Estou procurando uma camiseta esportiva azul.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Consulta semântica</td><td style="border: 1px solid #ccc; padding: 4px;">É improvável que as palavras da pergunta apareçam nos documentos. Similaridade conceitual entre a pergunta e os documentos.</td><td style="border: 1px solid #ccc; padding: 4px;">Estou procurando roupas para clima frio.</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Busca híbrida</td><td style="border: 1px solid #ccc; padding: 4px;">A questão contém componentes lexicais e/ou semânticos. Similaridade semântica e de tokens entre a pergunta e os documentos.</td><td style="border: 1px solid #ccc; padding: 4px;">Estou procurando um vestido tamanho P para um casamento na praia.</td>
                    </tr>
              </tbody>
            </table>
          <p>As partes <strong><em>lexicais </em></strong>da pergunta provavelmente fazem parte de títulos e descrições, ou nomes de categorias, enquanto as partes <strong><em>semânticas </em></strong>são conceitos relacionados a esses campos. <strong><em>&quot;Azul&quot;</em></strong> provavelmente será o nome de uma categoria ou parte de uma descrição, e <strong><em>&quot;casamento na praia&quot;</em></strong> provavelmente não será, mas pode estar semanticamente relacionado a roupas de linho.</p><h3>Consulta lexical (/search/lexic?q=)&lt;query_term&gt;</h3><p>A busca lexical, também chamada de busca de texto completo, significa pesquisar com base na similaridade de tokens; ou seja, após uma análise, os documentos que incluem os tokens da busca serão retornados.</p><p>Você pode conferir nosso tutorial prático de busca lexical <a href="https://www.elastic.co/demo-gallery/lexical-search">aqui</a>.</p><pre><code>app.get("/search/lexic", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      size: 5,
      body: {
        query: {
          multi_match: {
            query: q,
            fields: ["owner_name", "pet_name", "visit_details"],
          },
        },
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Testamos com: <strong><em>corte de unhas</em></strong></p><pre><code>curl http://localhost:3000/search/lexic?q=nail%20trimming</code></pre><p>Responder:</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 2.7075968,
            "_source": {
                "pet_name": "Mochi",
                "owner_name": "Daniel Kim",
                "species": "Rabbit",
                "visit_details": "Nail trimming and general health check. No issues.",
                "breed": "Mixed",
                "vaccination_history": []
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 2.560356,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        }
    ]
}</code></pre><h3>Consulta semântica&lt;query_term&gt; (/search/semantic?q=)</h3><p>A busca semântica, diferentemente da busca lexical, encontra resultados que são semelhantes ao significado dos termos de busca por meio de busca vetorial.</p><p>Você pode conferir nosso tutorial prático de busca semântica <a href="https://www.elastic.co/demo-gallery/semantic-search">aqui</a>.</p><pre><code>app.get("/search/semantic", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      size: 5,
      body: {
        query: {
          semantic: {
            field: "semantic_field",
            query: q
          },
        },
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits,
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Fizemos o teste com a seguinte pergunta: <strong><em>Quem fez pedicure?</em></strong></p><pre><code>curl http://localhost:3000/search/semantic?q=Who%20got%20a%20pedicure?</code></pre><p>Responder:</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 4.861466,
            "_source": {
                "owner_name": "Daniel Kim",
                "pet_name": "Mochi",
                "species": "Rabbit",
                "breed": "Mixed",
                "vaccination_history": [],
                "visit_details": "Nail trimming and general health check. No issues."
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 4.7152824,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9RY6RJYBLe2GoFQ6-9n9",
            "_score": 1.6717153,
            "_source": {
                "pet_name": "Rex",
                "owner_name": "Tomás Herrera",
                "species": "Dog",
                "visit_details": "Follow-up for previous leg strain, improving well.",
                "breed": "German Shepherd",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Leptospirosis"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9xY6RJYBLe2GoFQ6-9n9",
            "_score": 1.5600781,
            "_source": {
                "pet_name": "Simba",
                "owner_name": "Leo Martínez",
                "species": "Cat",
                "visit_details": "Dental cleaning. Minor tartar buildup removed.",
                "breed": "Maine Coon",
                "vaccination_history": [
                    "Rabies",
                    "Feline Panleukopenia"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "-BY6RJYBLe2GoFQ6-9n9",
            "_score": 1.2696637,
            "_source": {
                "pet_name": "Rocky",
                "owner_name": "Rachel Green",
                "species": "Dog",
                "visit_details": "Skin rash, antihistamines prescribed.",
                "breed": "Bulldog Mix",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus"
                ]
            }
        }
    ]
}</code></pre><h3>Consulta híbrida (/search/hybrid?q=)&lt;query_term&gt;</h3><p>A busca híbrida permite combinar a busca semântica e a busca lexical, obtendo assim o melhor dos dois mundos: a precisão da busca por token, juntamente com a proximidade de significado da busca semântica.</p><pre><code>app.get("/search/hybrid", async (req, res) => {
  const { q } = req.query;

  const INDEX_NAME = "vet-visits";

  try {
    const result = await esClient.search({
      index: INDEX_NAME,
      body: {
        retriever: {
          rrf: {
            retrievers: [
              {
                standard: {
                  query: {
                    bool: {
                      must: {
                         multi_match: {
             query: q,
            fields: ["owner_name", "pet_name", "visit_details"],
          },
                      },
                    },
                  },
                },
              },
              {
                standard: {
                  query: {
                    bool: {
                      must: {
                        semantic: {
                          field: "semantic_field",
                          query: q,
                        },
                      },
                    },
                  },
                },
              },
            ],
          },
        },
        size: 5,
      },
    });

    res.status(200).json({
      success: true,
      results: result.hits.hits,
    });
  } catch (error) {
    console.error("Error performing search:", error);

    res.status(500).json({
      success: false,
      results: null,
      error: error.message,
    });
  }
});</code></pre><p>Fizemos o teste com a pergunta: “<strong><em>Quem fez pedicure ou tratamento dentário?”</em></strong></p><pre><code>curl http://localhost:3000/search/hybrid?q=who%20got%20a%20pedicure%20or%20dental%20treatment</code></pre><p>Resposta.</p><pre><code>{
    "success": true,
    "results": [
        {
            "_index": "vet-visits",
            "_id": "9xY6RJYBLe2GoFQ6-9n9",
            "_score": 0.032522473,
            "_source": {
                "pet_name": "Simba",
                "owner_name": "Leo Martínez",
                "species": "Cat",
                "visit_details": "Dental cleaning. Minor tartar buildup removed.",
                "breed": "Maine Coon",
                "vaccination_history": [
                    "Rabies",
                    "Feline Panleukopenia"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "-RY6RJYBLe2GoFQ6-9n9",
            "_score": 0.016393442,
            "_source": {
                "pet_name": "Mochi",
                "owner_name": "Daniel Kim",
                "species": "Rabbit",
                "visit_details": "Nail trimming and general health check. No issues.",
                "breed": "Mixed",
                "vaccination_history": []
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8BY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015873017,
            "_source": {
                "pet_name": "Buddy",
                "owner_name": "Alice Johnson",
                "species": "Dog",
                "visit_details": "Annual check-up and nail trimming. Healthy and active.",
                "breed": "Golden Retriever",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Distemper"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "9RY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015625,
            "_source": {
                "pet_name": "Rex",
                "owner_name": "Tomás Herrera",
                "species": "Dog",
                "visit_details": "Follow-up for previous leg strain, improving well.",
                "breed": "German Shepherd",
                "vaccination_history": [
                    "Rabies",
                    "Parvovirus",
                    "Leptospirosis"
                ]
            }
        },
        {
            "_index": "vet-visits",
            "_id": "8xY6RJYBLe2GoFQ6-9n9",
            "_score": 0.015384615,
            "_source": {
                "pet_name": "Luna",
                "owner_name": "Jake Thompson",
                "species": "Dog",
                "visit_details": "Mild ear infection, cleaning and antibiotics given.",
                "breed": "Labrador Mix",
                "vaccination_history": [
                    "Rabies",
                    "Bordetella"
                ]
            }
        }
    ]
}</code></pre><h2>Conclusão</h2><p>Nesta primeira parte da nossa série, explicamos como configurar nosso ambiente e criar um servidor com diferentes endpoints de pesquisa para consultar os documentos do Elasticsearch, seguindo as melhores práticas de cliente/servidor. Confira <a href="https://www.elastic.co/search-labs/blog/how-to-use-elasticsearch-in-javascript-part-i">a segunda parte</a> da nossa série, na qual você aprenderá as melhores práticas de produção e como executar o cliente Elasticsearch Node.js em ambientes Serverless.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como configurar o número de réplicas em um índice do Elasticsearch]]></title>
            <description><![CDATA[Aprenda a configurar o number_of_replicas em um índice do Elasticsearch para melhorar o desempenho na busca e proporcionar resiliência contra falhas de nós. 
]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-index-number-of_replicas</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-index-number-of_replicas</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 14 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Elasticsearch foi projetado para ser um sistema distribuído capaz de lidar com grandes volumes de dados e fornecer alta disponibilidade. Uma das principais funcionalidades que permitem isso é o conceito de replicação de índice, que é controlado pela configuração <code>number_of_replicas</code> . Este artigo irá abordar em detalhes essa configuração, suas implicações e como configurá-la corretamente.</p><h2>O papel das réplicas no Elasticsearch</h2><p>No Elasticsearch, um índice é uma coleção de documentos que são particionados em vários shards primários. Cada fragmento primário é um índice Apache Lucene independente, e os documentos dentro de um índice são distribuídos entre todos os fragmentos primários. Para garantir alta disponibilidade e redundância de dados, o Elasticsearch permite que cada shard tenha uma ou mais cópias, conhecidas como réplicas.<br/><br/>A configuração <code>number_of_replicas</code> controla o número de shards de réplica (cópias) que o Elasticsearch cria para cada shard primário em um índice. Por padrão, o Elasticsearch cria uma réplica para cada shard primário, mas isso pode ser alterado de acordo com os requisitos do seu sistema.</p><h2>Configurando o número de réplicas</h2><p>A configuração <code>number_of_replicas</code> pode ser definida no momento da criação do índice ou atualizada posteriormente. Veja como você pode configurar isso durante a criação do índice:</p><pre><code>PUT /my_index
{
  "settings": {
    "number_of_replicas": 2
  }
}</code></pre><p>Neste exemplo, o Elasticsearch criará duas réplicas para cada shard primário no índice <code>my_index</code> .</p><p>Para atualizar a configuração <code>number_of_replicas</code> de um índice existente, você pode usar a API <code>_settings</code> :</p><pre><code>PUT /my_index/_settings
{
  "number_of_replicas": 3
}</code></pre><p>Este comando atualizará o índice <code>my_index</code> para ter três réplicas para cada fragmento primário.</p><h2>Implicações da configuração number_of_replicas</h2><p>A configuração <code>number_of_replicas</code> tem um impacto significativo no desempenho e na resiliência do seu <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-cluster/">cluster</a> Elasticsearch. Aqui estão alguns pontos importantes a serem considerados:</p><ol><li><strong>Redundância e disponibilidade de dados:</strong> Aumentar o <code>number_of_replicas</code> melhora a disponibilidade dos seus dados, criando mais cópias de cada fragmento. Se um nó falhar, o Elasticsearch ainda poderá fornecer dados a partir dos fragmentos de réplica nos <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-node/">nós</a> restantes.</li><li><strong>Desempenho de busca:</strong> Fragmentos de réplica podem atender solicitações de leitura, portanto, ter mais réplicas pode melhorar o desempenho de busca, distribuindo a carga por mais fragmentos.</li></ol><div style="display:none">Unknown block type "block", specify a component for it in the `components.types` option</div><ol><li><strong>Requisitos de armazenamento:</strong> Mais réplicas significam mais espaço de armazenamento. Você deve garantir que seu cluster tenha capacidade suficiente para armazenar as réplicas adicionais.</li><li><strong>Resiliência à falha do nó:</strong> O <code>number_of_replicas</code> deve ser definido considerando o número de nós em seu cluster. Se o <code>number_of_replicas</code> for igual ou maior que o número de nós, seu cluster pode tolerar a falha de vários nós sem perda de dados.</li></ol><h2>Melhores práticas para definir o número de réplicas</h2><p>A configuração ideal <code>number_of_replicas</code> depende dos requisitos específicos do seu sistema. No entanto, aqui estão algumas boas práticas gerais:</p><ul><li>Para um cluster de nó único, <code>number_of_replicas</code> deve ser definido como 0, pois não há outros nós para armazenar réplicas.</li><li>Para um cluster com vários nós, <code>number_of_replicas</code> deve ser definido como pelo menos 1 para garantir redundância de dados e alta disponibilidade.</li><li>Se o desempenho da pesquisa for uma prioridade, considere aumentar o <code>number_of_replicas</code>. No entanto, tenha em mente a relação de compromisso entre o desempenho de gravação e os requisitos de armazenamento.</li><li>Certifique-se sempre de que seu cluster tenha capacidade suficiente para armazenar as réplicas adicionais.</li></ul>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/mapping-embeddings-to-elasticsearch-field-types</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/mapping-embeddings-to-elasticsearch-field-types</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f98fdff837db55b466780c0bae672aa6f6c3a966-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><pre><code>{
  "knn": {
    "field": "my_dense_vector",
    "k": 10,
    "num_candidates": 50,
    "query_vector": [/* vector generated by model */]
  }
}</code></pre><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><pre><code>{
"script_score": {
    "query": { "match_all": {} },
    "script": {
      "source": "cosineSimilarity(params.query_vector,
'my_dense_vector') + 1.0",
      "params": {
        "query_vector": [/* vector */]
      }
    }
  }
}</code></pre><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><pre><code>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "query_vector": {
        "token1": 0.6,
        "token2": 0.2,
        "token3": 0.9
      }
    }
  }
}</code></pre><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><pre><code>{
  "query": {
    "sparse_vector": {
      "field": "field_sparse",
      "inference_id": "the inference ID to produce the token/weights",
      "query": "search text"
    }
  }
}</code></pre><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><pre><code>{
  "query": {
    "semantic": {
      "field": "semantic_text_field",
      "query": "search text"
    }
  }
}</code></pre><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><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-text.html">Tipo de campo de texto semântico</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sparse-vector.html">Tipo de campo vetorial esparso</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">Tipo de campo vetorial denso</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-semantic-query.html">Consulta semântica</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-sparse-vector-query.html">Consulta de vetor esparso</a></li><li><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html">pesquisa kNN</a></li><li><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></li><li><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></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Excluindo campos do Elasticsearch da indexação]]></title>
            <description><![CDATA[Aprenda como configurar o Elasticsearch para excluir campos, os principais motivos para excluir campos da indexação e as práticas recomendadas a serem seguidas.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/excluding-elasticsearch-fields-from-indexing</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/excluding-elasticsearch-fields-from-indexing</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Mon, 12 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/3aa0b481994d2445ba979d3c79fff64c5ee6676a-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>No Elasticsearch, indexação refere-se ao processo de armazenar e organizar dados de forma que eles possam ser facilmente pesquisados. Embora indexar todos os campos de um documento possa ser útil em alguns casos, existem situações em que você pode querer excluir determinados campos da indexação. Isso pode ajudar a melhorar o desempenho, reduzir os custos de armazenamento e minimizar o tamanho geral do seu índice Elasticsearch.</p><p>Neste artigo, discutiremos os motivos para excluir campos da indexação, como configurar o Elasticsearch para excluir campos específicos e algumas práticas recomendadas a serem seguidas ao fazer isso.</p><h2>Motivos para excluir campos da indexação</h2><ol><li><strong>Desempenho: </strong>Indexar todos os campos de um documento pode aumentar o tempo de indexação e tornar a pesquisa mais lenta. Ao excluir campos que não são necessários para pesquisa ou agregação, você pode melhorar o desempenho geral do seu cluster Elasticsearch.</li><li><strong>Armazenamento: </strong>A indexação de campos consome espaço de armazenamento. Excluir campos que não são necessários para pesquisa ou agregação pode ajudar a reduzir os requisitos de armazenamento do seu cluster Elasticsearch.</li><li><strong>Tamanho do índice: </strong>O tamanho de um índice do Elasticsearch está diretamente relacionado ao número de campos indexados. Ao excluir campos desnecessários, você pode minimizar o tamanho do seu índice, o que pode levar a um desempenho de pesquisa e indexação mais rápido.</li></ol><h2>Configurando o Elasticsearch para excluir campos</h2><p>Para excluir um campo da indexação no Elasticsearch, você pode usar a propriedade &quot;index&quot; no mapeamento do campo. Ao definir a propriedade “index” como “false”, o Elasticsearch não indexará o campo, e ele não será pesquisável nem estará disponível para agregações.</p><p>Aqui está um exemplo de como excluir um campo da indexação usando o mapeamento do Elasticsearch:</p><pre><code>PUT /my_index
{
  "mappings": {
    "properties": {
      "field_to_exclude": {
        "type": "text",
        "index": false
      }
    }
  }
}</code></pre><p>Neste exemplo, estamos criando um novo índice chamado “my_index” com um único campo chamado “field_to_exclude”. Ao definir a propriedade “index” como “false”, estamos dizendo ao Elasticsearch para não indexar esse campo. O campo ainda estará disponível no documento original.</p><h2>Melhores práticas para excluir campos da indexação</h2><ol><li><strong>Analise seus dados: </strong>Antes de excluir campos da indexação, é essencial analisar seus dados e entender quais campos são necessários para pesquisa e agregação. Isso ajudará você a tomar decisões informadas sobre quais campos excluir.</li><li><strong>Teste suas alterações: </strong>Ao excluir campos da indexação, é crucial testar as alterações para garantir que a funcionalidade de pesquisa e agregação continue funcionando conforme o esperado. Isso pode ajudar você a evitar problemas inesperados ou falhas de desempenho.</li><li><strong>Monitore o desempenho:</strong> após excluir campos da indexação, monitore o desempenho do seu cluster Elasticsearch para garantir que as alterações tenham surtido o efeito desejado. Isso pode ajudar a identificar quaisquer otimizações adicionais que possam ser necessárias.</li><li><strong>Utilize a filtragem por origem:</strong> Se você precisa armazenar um campo no Elasticsearch, mas não deseja que ele seja pesquisável ou disponível para agregações, considere usar a filtragem por origem. Isso permite armazenar o campo no campo _source, mas excluí-lo do índice.</li></ol><h2>Conclusão</h2><p>Excluir campos da indexação no Elasticsearch pode ajudar a melhorar o desempenho, reduzir os custos de armazenamento e minimizar o tamanho geral do seu índice. Ao analisar cuidadosamente seus dados e entender quais campos são necessários para pesquisa e agregação, você pode tomar decisões informadas sobre quais campos excluir. Sempre teste suas alterações e monitore o desempenho do seu cluster Elasticsearch para garantir que suas otimizações tenham o efeito desejado.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Excluindo um campo de um documento no Elasticsearch]]></title>
            <description><![CDATA[Saiba como excluir campos de documentos do Elasticsearch usando a API de atualização, scripts ou reindexação para remoções únicas e em massa.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-delete-field-from-document</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-delete-field-from-document</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/89278eb7309b7f3018c61be2b514d1fd25b9564d-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>No Elasticsearch, é comum precisar excluir um campo de um documento. Isso pode ser útil quando você deseja remover informações desnecessárias ou desatualizadas do seu índice. Neste artigo, discutiremos diferentes métodos para excluir um campo de um documento no Elasticsearch, juntamente com exemplos e instruções passo a passo. </p><h2>Método 1: Usando a API de atualização</h2><p>A <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/update-document">API de atualização</a> fornece um script que modifica a origem de um documento para atualizar o documento. Você pode usar essa API para apagar o campo de um documento, bastando deixar o campo como &quot;null&quot;. Veja como fazer isso:</p><p>1. Identifique o índice, o tipo de documento (se estiver usando o Elasticsearch 6.x ou anterior) e o ID do documento que você deseja atualizar.</p><p>2. Utilize a API de atualização com um script que defina o campo como nulo ou, melhor ainda, que o remova do documento de origem. O exemplo a seguir demonstra como excluir o campo “field_to_delete” de um documento com ID “1” no índice “my_index”:</p><pre><code>POST /my_index/_update/1
{
  "script": "ctx._source.remove('field_to_delete')"
}</code></pre><p>3. Execute a solicitação. Se a operação for bem-sucedida, o Elasticsearch retornará uma resposta indicando que o documento foi atualizado.</p><p>Nota: Este método apenas remove o campo do documento especificado. O campo ainda existirá no mapeamento e em outros documentos do índice.</p><h2>Método 2: Reindexação com uma fonte modificada</h2><p>Para apagar um campo de todos os documentos em um índice, você pode usar a <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-reindex">API de reindexação</a> para criar um novo índice com a fonte modificada. Veja como:</p><p>1. Crie um novo índice com as mesmas configurações e mapeamentos do índice original. Você pode usar a API Get Index para recuperar as configurações e os mapeamentos do índice original.</p><p>2. Utilize a API Reindex para copiar documentos do índice original para o novo índice, removendo o campo da origem. O exemplo a seguir demonstra como excluir o campo “field_to_delete” de todos os documentos no índice “my_index”:</p><pre><code>POST /_reindex
{
  "source": {
    "index": "my_index"
  },
  "dest": {
    "index": "new_index"
  },
  "script": {
    "source": "ctx._source.remove('field_to_delete')"
  }
}</code></pre><p><br/>3. Verifique se o novo índice contém os documentos corretos com o campo removido.</p><p>4. Se tudo estiver correto, você pode excluir o índice original e, se necessário, adicionar um alias ao novo índice com o mesmo nome do índice original.</p><h2>Método 3: atualizando o mapeamento e reindexando</h2><p>Se você deseja excluir um campo do mapeamento e todos os documentos em um índice, pode atualizar o mapeamento e, em seguida, reindexar os documentos. Eis como fazer isso:</p><p>1. Crie um novo índice com as mesmas configurações do índice original.</p><p>2. Recupere os mapeamentos do índice original usando a API Get Mapping.</p><p>3. Modifique os mapeamentos removendo o campo que deseja excluir.</p><p>4. Aplique os mapeamentos modificados ao novo índice usando a API Put Mapping.</p><p>5. Utilize a API Reindex para copiar documentos do índice original para o novo índice, conforme descrito no Método 2.</p><p>6. Verifique se o novo índice contém os documentos corretos com o campo removido e se o campo não está presente no mapeamento.</p><p>7. Se tudo estiver correto, você pode apagar o índice original e, se necessário, adicionar um alias ao novo índice com o nome do índice original.</p><h2>Conclusão</h2><p>Neste artigo, discutimos três métodos para excluir um campo de um documento no Elasticsearch: usando a API de atualização, reindexando com uma fonte modificada e atualizando o mapeamento e reindexando. Cada método tem seus próprios casos de uso e vantagens e desvantagens, portanto, escolha aquele que melhor se adapta às suas necessidades. Lembre-se sempre de testar as alterações e verificar os resultados antes de aplicá-las em ambientes de produção.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como unir dois índices no Elasticsearch]]></title>
            <description><![CDATA[Explicando como usar os termos consulta, filtro Elasticsearch do Logstash, processador de enriquecimento e ES|QL para unir dois índices no Elasticsearch.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-join-two-indexes</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-join-two-indexes</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Carly Richmond]]></dc:creator>
            <pubDate>Wed, 07 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0d4736d10fa3e12e6233cd59993299c7bd48911b-680x450.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>No Elasticsearch, unir dois índices não é tão simples quanto em bancos de dados relacionais SQL tradicionais. No entanto, é possível obter resultados semelhantes usando certas técnicas e recursos fornecidos pelo Elasticsearch.</p><p>Historicamente, muitas pessoas usavam o<a href="https://www.elastic.co/pt/docs/reference/elasticsearch/mapping-reference/nested"> tipo de campo</a> <a href="https://www.elastic.co/pt/docs/reference/elasticsearch/mapping-reference/nested"><code>nested</code></a>como um mecanismo para unir diferentes índices. No entanto, era limitado devido a consultas caras e suporte incompleto no Kibana, especificamente visualizações do Lens.</p><p>Este artigo se aprofundará no processo de junção de dois índices no Elasticsearch, com foco nas seguintes abordagens: </p><ol><li>Usando a consulta <code>terms</code></li><li>Usando o processador <code>enrich</code> em pipelines de ingestão</li><li>Plug-in de filtro Logstash <code>elasticsearch</code></li><li>ES|QL <code>ENRICH</code></li><li>ES|QL <code>LOOKUP JOIN</code></li></ol><h2>Usando os termos consulta</h2><p>A <a href="https://www.elastic.co/pt/docs/reference/query-languages/query-dsl/query-dsl-terms-query">consulta de termos</a> é uma das maneiras mais eficazes de unir dois índices no Elasticsearch. Esta consulta é usada para recuperar documentos que contêm um ou mais termos exatos em um campo específico. Aqui discutimos como usá-lo para unir dois índices.</p><p>Primeiro, você precisa recuperar os dados necessários do primeiro índice. Isso pode ser feito usando uma simples solicitação GET e extraindo os valores do atributo <code>_source</code> .</p><pre><code># Simple GET request
GET first_index/_search</code></pre><p>Depois de obter os dados do primeiro índice, você pode usá-los para consultar o segundo índice. Isso é feito usando a consulta <code>terms</code> , onde você especifica o campo e os valores que deseja corresponder.</p><p>Aqui está um exemplo:</p><pre><code>GET second_index/_search
{
  "query": {
    "terms": {
      "field_in_second_index": ["value1_from_first_index", "value2_from_first_index"]
    }
  }
}</code></pre><p><br/>Neste exemplo, <code>field_in_second_index</code> é o campo no segundo índice que você deseja corresponder aos valores do primeiro índice. <code>value1_from_first_index</code> e <code>value2_from_first_index</code> são os valores do primeiro índice que você deseja corresponder no segundo índice.</p><p>A consulta de termos também fornece suporte para executar as duas etapas acima de uma só vez usando uma técnica chamada <a href="https://www.elastic.co/pt/docs/reference/query-languages/query-dsl/query-dsl-terms-query#query-dsl-terms-lookup">pesquisa de termos</a>. O Elasticsearch se encarregará de recuperar de forma transparente os valores correspondentes de outro índice. Por exemplo, se você tiver um índice de equipes contendo uma lista de jogadores:</p><pre><code>PUT teams/_doc/team1
{
  "players":   ["john", "bill", "michael"]
}
PUT teams/_doc/team2
{
  "players":   ["aaron", "joe", "donald"]
}</code></pre><p>É possível consultar um índice de pessoas para todas as pessoas que jogam no time1, conforme mostrado abaixo:</p><pre><code>GET people/_search?pretty
{
  "query": {
    "terms": {
        "name" : {
            "index" : "teams",
            "id" : "team1",
            "path" : "players"
        }
    }
  }
}</code></pre><p>No exemplo acima, o Elasticsearch recuperará de forma transparente os nomes dos jogadores do documento com id team1 no índice de equipes (ou seja, “john”, “bill” e “michael”) e encontrar todos os documentos no índice de pessoas que contenham qualquer um desses valores no campo de nome.</p><p>Para os curiosos, a consulta SQL equivalente seria:</p><pre><code>SELECT p.* FROM people p
INNER JOIN teams t ON p.name = t.players</code></pre><h2>Usando o processador de enriquecimento</h2><p>O<a href="https://www.elastic.co/pt/docs/reference/enrich-processor/enrich-processor"> processador</a> <a href="https://www.elastic.co/pt/docs/reference/enrich-processor/enrich-processor"><code>enrich</code></a>é outra ferramenta poderosa que pode ser usada para unir dois índices no Elasticsearch. Este processador enriquece os dados de documentos recebidos adicionando dados de um índice de enriquecimento predefinido.</p><p>Veja como você pode usar o processador de enriquecimento para unir dois índices:</p><p>1. Primeiro, você precisa criar uma política de enriquecimento. Esta política define qual índice usar para enriquecimento, qual campo corresponder e qual(is) campo(s) usar para enriquecimento de documentos recebidos.</p><p>Aqui está um exemplo:</p><pre><code>PUT _enrich/policy/my_enrich_policy
{
  "match": {
    "indices": "first_index",
    "match_field": "field_in_first_index",
    "enrich_fields": ["field_to_enrich"]
  }
}</code></pre><p>2. Depois que a política for criada, você precisa executá-la para criar o índice de enriquecimento a partir da sua política recém-criada:</p><pre><code>PUT _enrich/policy/my_enrich_policy/_execute</code></pre><p>Isso criará um novo índice enriquecido oculto que será usado durante o enriquecimento. Dependendo do tamanho do índice de origem, esta operação pode levar algum tempo. Certifique-se de que a política de enriquecimento esteja totalmente criada antes de prosseguir para a próxima etapa.</p><p>3. Após a criação da política de enriquecimento, você pode usar o processador de enriquecimento em um pipeline de ingestão para enriquecer os dados de documentos recebidos:</p><pre><code>PUT _ingest/pipeline/my_pipeline
{
  "processors": [
    {
      "enrich": {
        "policy_name": "my_enrich_policy",
        "field": "field_in_second_index",
        "target_field": "enriched_field"
      }
    }
  ]
}</code></pre><p>Neste exemplo, <code>field_in_second_index</code> é o campo no segundo índice que precisa corresponder ao <code>match_field</code> do primeiro índice. <code>enriched_field</code> é o novo campo no segundo índice que conterá os dados enriquecidos do <code>enrich_fields</code> no primeiro índice.</p><p>Uma desvantagem dessa abordagem é que, se os dados mudarem em <code>first_index</code>, a política de enriquecimento precisará ser reexecutada. O índice enriquecido não é atualizado ou sincronizado automaticamente a partir do índice de origem do qual foi criado. Entretanto, se <code>first_index</code> for relativamente estável, então essa abordagem funciona bem.</p><h2>Plug-in de filtro ElasticSearch do Logstash</h2><p>Se estiver usando o Logstash, outra opção semelhante ao processador <code>enrich</code> descrito acima é usar o plug-in de filtro <code>elasticsearch</code> para adicionar campos relevantes ao evento com base em uma consulta especificada. A configuração do nosso pipeline Logstash residiria em um arquivo <code>.conf</code> , como <code>my-pipeline.conf</code>.</p><p>Vamos imaginar que nosso pipeline está extraindo logs do Elasticsearch usando o<a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-inputs-elasticsearch"> plugin de entrada</a> <a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-inputs-elasticsearch"><code>elasticsearch</code></a> , com uma consulta para restringir a seleção:</p><pre><code>input {
  # Read all documents from Elasticsearch matching the given query
  elasticsearch {
    hosts => "localhost"
    query => '{ "query": { "match": { "statuscode": 200 } }, "sort": [ "_doc" ] }'
  }
}</code></pre><p>Se quisermos enriquecer essas mensagens com informações de um determinado índice, podemos usar o<a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-filters-elasticsearch"> plugin de filtro</a> <a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-filters-elasticsearch"><code>elasticsearch</code></a>na seção <code>filter</code> para enriquecer nossos logs:</p><pre><code>filter {
   elasticsearch {
      hosts => ["localhost"]
      index => "index_name"
      query => "type:start AND operation:%{[opid]}"
      fields => { "@timestamp" => "started" }
   }
}</code></pre><p>O código acima encontrará os documentos do índice <code>index_name</code> onde <code>type</code> é o início e o campo de operação corresponde ao <code>opid</code> especificado e, em seguida, copiará o valor do campo <code>@timestamp</code> em um novo campo chamado <code>started</code>.</p><p>Os documentos enriquecidos seriam então enviados para a fonte de saída apropriada, neste caso para o Elasticsearch usando o<a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-outputs-elasticsearch"> plugin de saída</a> <a href="https://www.elastic.co/pt/docs/reference/logstash/plugins/plugins-outputs-elasticsearch"><code>elasticsearch</code></a> :</p><pre><code>output {
    elasticsearch {
        hosts => "localhost"
        data_stream => "true"
    }
}</code></pre><p>Se você já estiver usando o Logstash, esta opção pode ser útil para consolidar sua lógica de enriquecimento em um único lugar e processá-la conforme novos eventos chegam. No entanto, se você não fizer isso, isso adicionará complexidade à sua solução e será outro componente que você precisa executar e manter.</p><h2>ES|QL ENRIQUECER</h2><p>A introdução do <a href="https://www.elastic.co/pt/docs/explore-analyze/query-filter/languages/esql">ES|QL</a>, que foi disponibilizado gratuitamente na versão 8.14, é uma linguagem de consulta canalizada suportada pelo Elasticsearch que permite a filtragem, transformação e análise de dados. Usar o comando de processamento ENRICH nos permite adicionar dados de índices existentes usando uma política de enriquecimento.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/03aadddc08afffff3f6526c9c052999c97fa09dd-1600x989.png" style="width: 100%;" /><p>Tomando a mesma política <code>my_enrich_policy</code> do exemplo original do processador de enriquecimento, o exemplo ES|QL ficaria assim:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| ENRICH my_enrich_policy</code></pre><p>Também é possível substituir os campos de correspondência e enriquecimento, que em nosso exemplo são <code>field_in_first_index</code> e <code>field_to_enrich</code> respectivamente:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| ENRICH my_enrich_policy ON another_field_in_first_index WITH different_field_to_enrich</code></pre><p>Embora a limitação óbvia seja que você precisa especificar uma política de enriquecimento primeiro, o ES|QL oferece a flexibilidade de ajustar os campos conforme necessário.</p><h2>ES|QL LOOKUP JOIN</h2><p>O Elasticsearch 8.18 apresenta uma nova maneira de unir índices no Elasticsearch, o comando <code>LOOKUP JOIN</code> . Este comando opera como um LEFT OUTER JOIN no estilo SQL usando o novo <a href="https://www.elastic.co/pt/docs/reference/elasticsearch/index-settings/index-modules#index-mode-setting">modo de índice de pesquisa</a> no lado direito da junção.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/1d73495979c4d6bb675c4c966ea86d9a72dc1c48-510x605.png" style="width: 100%;" /><p>Revisitando nosso exemplo anterior, a nova consulta é a seguinte, onde <code>match_field</code> precisa estar presente em <code>first_index</code> e <code>second_index</code>:</p><pre><code>FROM first_index
| WHERE field_in_first_index IS NOT NULL
| LOOKUP JOIN second_index ON match_field</code></pre><p>A vantagem do LOOKUP JOIN sobre as outras abordagens é que ele não requer nenhuma política <code>enrich</code> e, portanto, o processamento adicional associado à configuração da política. É útil ao trabalhar com dados de enriquecimento que mudam frequentemente, diferentemente das outras abordagens discutidas neste artigo.</p><h2>Conclusão</h2><p>Concluindo, embora o Elasticsearch não suporte operações de junção tradicionais, ele fornece vários recursos que podem ser usados para obter resultados semelhantes. Especificamente, abordamos como realizar operações de junção usando:</p><ol><li>A consulta <code>terms</code></li><li>O processador <code>enrich</code> em pipelines de ingestão</li><li>Plug-in de filtro Logstash <code>elasticsearch</code></li><li>ES|QL <code>ENRICH</code></li><li>ES|QL <code>LOOKUP JOIN</code></li></ol><p>É importante observar que esses métodos têm suas limitações e devem ser usados criteriosamente com base nos requisitos específicos e na natureza dos dados.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Entendendo a pontuação do Elasticsearch e a API Explain.]]></title>
            <description><![CDATA[Saiba mais sobre os mecanismos de pontuação do Elasticsearch e a função prática de pontuação para auditar a relevância da busca e melhorar a classificação de documentos com a API Explain.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-scoring-and-explain-api</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-scoring-and-explain-api</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/a70c5403064d5bbceff66a17373332362227f13c-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O Elasticsearch é um mecanismo de busca poderoso que fornece resultados de pesquisa rápidos e relevantes, calculando uma pontuação para cada documento no índice. Essa pontuação é um fator crucial para determinar a ordem dos resultados da pesquisa. Neste artigo, vamos nos aprofundar no mecanismo de pontuação do Elasticsearch e explorar a API Explain, que ajuda a compreender o processo de pontuação.</p><h2>Mecanismos de pontuação no Elasticsearch</h2><p>O Elasticsearch utiliza, por padrão, um modelo de pontuação chamado Practical Scoring Function (BM25). Este modelo é baseado na teoria probabilística de recuperação de informação e leva em consideração fatores como frequência de termos, frequência inversa de documentos e normalização do comprimento do campo. Vamos discutir brevemente esses fatores:</p><ol><li><strong>Frequência do termo (TF):</strong> Representa o número de vezes que um termo aparece em um documento. Uma maior frequência de um termo indica uma relação mais forte entre o termo e o documento.</li><li><strong>Frequência Inversa de Documentos (IDF):</strong> Este fator mede a importância de um termo em toda a coleção de documentos. Um termo que aparece em muitos documentos é considerado menos importante, enquanto um termo que aparece em menos documentos é considerado mais importante.</li><li><strong>Normalização do comprimento do campo</strong>: Este fator leva em consideração o comprimento do campo no qual o termo aparece. Campos mais curtos recebem maior peso, pois o termo é considerado mais significativo em um campo mais curto.</li></ol><h2>Usando a API Explain</h2><p>A API Explain do Elasticsearch é uma ferramenta valiosa para entender o processo de pontuação. Fornece uma explicação detalhada de como a pontuação de um documento específico foi calculada. Para usar a API Explain, você precisa enviar uma solicitação GET para o seguinte endpoint:</p><pre><code>GET /<index>/_explain/<document_id></code></pre><p>No corpo da solicitação, você precisa fornecer a consulta para a qual deseja entender a pontuação. Eis um exemplo:</p><pre><code>{
  "query": {
    "match": {
      "title": "elasticsearch"
    }
  }
}</code></pre><p>A resposta da API Explain incluirá uma descrição detalhada do processo de pontuação, incluindo os fatores individuais (TF, IDF e normalização do comprimento do campo) e suas contribuições para a pontuação final. Eis um exemplo de resposta:</p><pre><code>{
  "_index": "example_index",
  "_type": "_doc",
  "_id": "1",
  "matched": true,
  "explanation": {
    "value": 1.2,
    "description": "weight(title:elasticsearch in 0) [PerFieldSimilarity], result of:",
    "details": [
      {
        "value": 1.2,
        "description": "score(doc=0,freq=1.0 = termFreq=1.0\n), product of:",
        "details": [
          {
            "value": 2.2,
            "description": "idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:",
            "details": [
              {
                "value": 1,
                "description": "docFreq",
                "details": []
              },
              {
                "value": 1,
                "description": "docCount",
                "details": []
              }
            ]
          },
          {
            "value": 0.5,
            "description": "tfNorm, computed as (freq * (k1 + 1)) / (freq + k1 * (1 - b + b * fieldLength / avgFieldLength)) from:",
            "details": [
              {
                "value": 1,
                "description": "termFreq=1.0",
                "details": []
              },
              {
                "value": 1.2,
                "description": "parameter k1",
                "details": []
              },
              {
                "value": 0.75,
                "description": "parameter b",
                "details": []
              },
              {
                "value": 1,
                "description": "avgFieldLength",
                "details": []
              },
              {
                "value": 1,
                "description": "fieldLength",
                "details": []
              }
            ]
          }
        ]
      }
    ]
  }
}</code></pre><p>Neste exemplo, a resposta mostra que a pontuação de 1,2 é um produto do valor IDF (2,2) e do valor tfNorm (0,5). A explicação detalhada ajuda a compreender os fatores que contribuem para a pontuação e pode ser útil para refinar a relevância da pesquisa.</p><h2>Conclusão</h2><p>A pontuação do Elasticsearch é um aspecto crucial para fornecer resultados de pesquisa relevantes. Ao entender os mecanismos de pontuação e usar a API Explain, você pode obter insights sobre os fatores que afetam os resultados da pesquisa e otimizar suas consultas de pesquisa para obter maior relevância e desempenho.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Criação de modelos de índice no Elasticsearch: Como usar modelos compostos]]></title>
            <description><![CDATA[Veja como criar templates componíveis e de componentes no Elasticsearch para garantir mapeamentos consistentes e automatizar a configuração dos índices.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/index-composable-templates</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/index-composable-templates</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/510750708df50bf79463586a1bbf35bf94acfa30-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Um índice do Elasticsearch pode ser configurado por meio de mapeamento, configurações e aliases: </p><ul><li>As definições de mapeamento especificam o esquema de dados.</li><li>As configurações definem o tamanho dos fragmentos e as taxas de atualização. </li><li>Os aliases são usados para dar nomes alternativos ao índice.</li></ul><p>Ao indexar um documento pela primeira vez ou criar um índice vazio usando a API Criar Índice, o índice será criado com as configurações padrão, sem esquema de dados e sem aliases. Essas configurações padrão funcionam muito bem em ambientes de desenvolvimento e teste, mas talvez seja necessário personalizar nossos índices para ambientes de produção.</p><p>Trabalhar com os mapeamentos e configurações padrão em produção pode resultar em indexação e desempenho de pesquisa deficientes. A criação manual de índices é um processo tedioso e demorado. Recriar esses índices em todos os ambientes é especialmente impraticável se tivermos um esquema de mapeamento complexo, além de configurações e aliases personalizados.</p><p>Felizmente, o Elasticsearch nos fornece uma ferramenta para aplicar automaticamente uma configuração predefinida ao criar índices na forma de modelos <em>de índice</em> <em>.</em></p><h2>Modelos de índice</h2><p>Os modelos de índice permitem criar índices com configurações definidas pelo usuário. Um índice pode obter a configuração desses modelos, por exemplo, um número definido de shards e réplicas ou mapeamentos de campos, durante sua instanciação. Um modelo será definido com um padrão de nome e algumas configurações. Se o nome do índice corresponder ao padrão de nomenclatura do modelo, o novo índice será criado com a configuração definida no modelo.</p><p>O Elasticsearch atualizou sua funcionalidade de modelos na versão 7.8 com modelos compostos. Esta versão mais recente oferece muito mais modelos de índice reutilizáveis, como demonstrado neste artigo.</p><h3>Tipos de modelo de índice</h3><p>Os modelos de índice podem ser classificados em duas categorias:</p><ul><li><strong>Modelos de índice (ou modelos de índice componíveis)</strong>: Os modelos de índice componíveis podem existir por si só ou podem ser compostos por nenhum ou mais modelos componentes (consulte a segunda categoria).</li><li><strong>Modelos de componentes:</strong> O modelo de componente é um modelo <em>reutilizável</em> que define a configuração necessária. Normalmente, espera-se que o modelo de componente esteja associado a um modelo de índice. Cada um dos modelos de componentes pode ser associado a um ou mais modelos de índice. </li></ul><p>Como você pode ver na imagem abaixo, os modelos de índice A e B compartilham modelos de componentes (neste caso, apenas um – o Modelo 3) entre si. Um modelo de índice pode não conter nenhum ou vários modelos de componentes, e cada um dos modelos de componentes pode não estar associado a nenhum ou a vários modelos de índice. Ambos os tipos de modelos podem existir por si só, porém os modelos de componentes são inúteis a menos que estejam anexados a um modelo de índice.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/96c0aac29d3992e54a79be34e14cf909e0ca2ea9-1202x556.png" style="width: 100%;" /><p>A ideia geral é desenvolver um catálogo de modelos de componentes para uma organização usar em diversas necessidades (por exemplo, especificar os vários modelos de componentes para ambientes individuais) e associá-los a vários índices por meio de modelos de índice componíveis.</p><h2>Como criar modelos (de índice) componíveis</h2><p>O Elasticsearch fornece um endpoint _index_template para gerenciar modelos de índice. Neste modelo, o usuário fornece todos os mapeamentos, configurações e aliases necessários, juntamente com um padrão de nome de índice. Vamos analisar um exemplo de criação de um modelo para um aplicativo de microsserviços chamado <em>customer-order-service</em> , responsável pela lógica de geração de pedidos. </p><p>Digamos que nossa necessidade seja criar um modelo para pedidos de clientes, representado por um padrão com caracteres curinga: *pedidos. Espera-se que este modelo tenha determinados mapeamentos e configurações, como o campo order_date, bem como números de shards e réplicas.</p><p>Qualquer índice que corresponda a este modelo durante a sua criação herdará as configurações definidas neste modelo. Por exemplo, um índice black_friday_orders terá o campo order_date, o número de shards será definido como 5 e o número de réplicas como 2. Além disso, <em>todos</em> os índices criados a partir deste modelo também herdarão um único nome <a href="https://opster.com/guides/elasticsearch/glossary/elasticsearch-alias/">de alias</a> ! Vamos criar este modelo de pedidos (orders_template) com um padrão de índice definido como *orders e com um esquema de mapeamento que consiste em um único campo order_date com um formato de data predefinido dd-MM-yyyy. O código abaixo mostra como criar esse modelo de índice.</p><pre><code>PUT _index_template/orders_template
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    },
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    },
    "aliases":{
      "all_orders":{}
    }
  }
}</code></pre><p>Ao executar essa consulta nas DevTools do Kibana, o modelo é criado com o padrão de índice *orders, juntamente com o mapeamento predefinido, as configurações e um alias. O `index_patterns` é uma matriz de padrões de correspondência; qualquer índice que corresponda a esse padrão derivará a configuração do modelo. Você pode executar o seguinte comando para recuperar o modelo persistido, que deverá reiterar o que fizemos:</p><pre><code>GET _index_template/orders_template </code></pre><p>Existe também uma prioridade, um número positivo, definida ao criar o atributo de modelo definido no modelo: cada modelo é definido com uma prioridade, de forma que quaisquer alterações conflitantes de modelos diferentes sejam resolvidas usando esse valor, com precedência dada ao valor de prioridade mais alto. A seguir, analisaremos a prioridade dos modelos com mais detalhes.</p><h2>Criando um índice com o modelo</h2><p>Agora que temos um modelo – um projeto para criar índices – o próximo passo é criar um índice. Quando o nome do índice corresponde ao padrão fornecido, as configurações do modelo são aplicadas automaticamente. Para comprovar esse ponto, como mostra o código abaixo, vamos criar um novo índice chamado: blackfriday_orders:</p><pre><code>PUT blackfriday_orders</code></pre><p>Como o nome do índice (blackfriday_orders) corresponde ao padrão de nomenclatura definido no modelo (ou seja, *pedidos), o índice deve obter toda a configuração derivada do modelo. Vamos recuperar esse índice recém-criado e verificar se isso é realmente verdade executando o seguinte código:</p><pre><code>GET blackfriday_orders</code></pre><p>Isso deve retornar:</p><pre><code>{
  "blackfriday_orders" : {
    "aliases" : {
      "all_orders" : { }
    },
    "mappings" : {
      "properties" : {
        "order_date" : {
          "type" : "date",
          "format" : "dd-MM-yyyy"
        }
      }
    },
    "settings" : {
      "index" : {
         ...
        "number_of_shards" : "5",
        "number_of_replicas" : "2"
      }
    }
  }
}</code></pre><p>Conforme indicado na resposta, a configuração do blackfriday_orders foi herdada do modelo. Podemos tentar várias combinações de índices que herdarão com sucesso a configuração do modelo:</p><pre><code>PUT blackfriday_orders
PUT americaorders
PUT cancelled--orders
PUT undefined101orders</code></pre><p>No entanto, os seguintes índices não herdarão a configuração, pois o nome não corresponderá ao padrão:</p><pre><code>PUT blackfriday_orders2
PUT open_orders_
PUT allorders_total</code></pre><p>Um ponto importante a lembrar é que todos os índices derivados de um modelo têm o mesmo alias – all_orders – neste caso. Existe uma vantagem em ter um alias desse tipo: podemos simplesmente consultar esse único alias em vez de vários índices.</p><pre><code>GET blackfriday_orders,americaorders,undefined101orders/_search
GET all_orders/_search 
{
  "query": {
    "range": {
      "order_date": {
        "gte": "01-12-2021",
        "lte": "31-12-2021"
      }
    }
  }
}</code></pre><p>Embora criemos um modelo para *pedidos, espera-se que qualquer índice correspondente adote a configuração do modelo. Normalmente, consciente ou inconscientemente, as equipes podem criar alguns modelos adicionais por diversos motivos. Isso significa que, às vezes, o nome do índice pode corresponder a dois padrões de modelo diferentes! O Elasticsearch precisa decidir qual das configurações desses modelos deve ser aplicada. Felizmente, esse dilema pode ser resolvido usando a prioridade do modelo.</p><h2>Como criar modelos de componentes</h2><p>Aprendemos sobre modelos de índice na parte anterior deste artigo. Existem algumas desvantagens em criar modelos com a configuração já integrada – uma delas é que a configuração não pode ser exportada para outros modelos. Se desejarmos ter uma configuração semelhante, por exemplo, para modelos relacionados a clientes (*clientes), talvez tenhamos que recriar todo o modelo. Isso significa que podemos estar criando dezenas deles em uma organização típica (e você pode ter alguns outros dependendo do ambiente).</p><p>Como sempre buscamos a reutilização, o Elasticsearch redesenhou os modelos levando isso em consideração. Os modelos de componentes atendem a essa necessidade. Se você tem experiência em DevOps, provavelmente precisará criar índices com uma configuração predefinida para cada um dos ambientes. Em vez de aplicar manualmente cada uma dessas configurações de forma tediosa, você pode criar um modelo de componente para cada um dos ambientes.</p><p>Um modelo de componente nada mais é do que um bloco reutilizável de configurações que podemos usar para criar mais modelos de índice. Note que os modelos de componentes não têm utilidade a menos que sejam combinados com modelos de índice. Eles são expostos através de um endpoint _component_template. Vamos ver como tudo isso se encaixa.</p><h3>Configurações em um modelo de índice</h3><p>Vamos extrair as configurações que definimos anteriormente em nosso modelo de índice e criar um modelo de componente a partir delas. Espera-se que o settings_component_template tenha cinco shards primários com duas réplicas por shard primário. O primeiro passo, como mostra o código abaixo, é declarar e executar um modelo de componente com essa configuração.</p><pre><code>PUT _component_template/settings_component_template
{
  "template":{
    "settings":{
      "number_of_shards":5,
      "number_of_replicas":2
    }
  }
}</code></pre><p>Como mostra o código acima, usamos o endpoint _component_template para criar um modelo de componente. O corpo da solicitação contém as informações do modelo em um objeto de modelo. O modelo settings_component_template agora está disponível para uso em outros locais nos modelos de índice. Uma diferença notável é que este modelo não define nenhum padrão de índice; é simplesmente um bloco de código que configura algumas propriedades para nós.</p><h3>Modelo de mapeamento</h3><p>Da mesma forma, vamos criar outro modelo. Desta vez, vamos extrair o esquema de mapeamento que definimos anteriormente nos modelos de índice independentes. O código abaixo mostra o script:</p><pre><code>PUT _component_template/mappings_component_template
{
  "template": {
    "mappings": {
      "properties": {
        "order_date": {
          "type": "date",
          "format":"dd-MM-yyyy"
        }
      }
    }
  }
}</code></pre><h3>Modelo de aliases</h3><p>Seguindo a mesma linha de raciocínio, também podemos ter um modelo de componente com os aliases – dois aliases (all_orders e sales_orders):</p><pre><code>PUT _component_template/aliases_component_template
{
  "template": {
    "aliases": {
      "all_orders": {},
      "sales_orders":{}
    }
  }
}</code></pre><h3>Modelo de índice componível</h3><p>Agora que temos esses três modelos de componentes, o próximo passo é colocá-los em uso. Podemos fazer isso permitindo que um modelo de índice, digamos, para pedidos de Natal, o utilize:</p><pre><code>PUT _index_template/composed_orders_template
{
  "index_patterns": [
    "*orders"
  ],
  "priority": 500,
  "composed_of": [
    "settings_component_template",
    "mappings_component_template",
    "aliases_component_template"
  ]
}</code></pre><p>A tag `composed_of` é uma coleção de todos os modelos de componentes que compõem este modelo. Neste caso, estamos escolhendo as configurações, os mapeamentos e os modelos de componentes de aliases. Também estamos aumentando a prioridade, então este modelo terá precedência sobre qualquer outro. Assim que o modelo estiver pronto, quaisquer índices que correspondam ao padrão *orders herdarão a configuração desses três modelos de componentes.</p><p>Dito isso, caso desejemos criar um novo modelo, digamos, para clientes, utilizando apenas um dos modelos existentes (settings_component_template) e um modelo de aliases recém-criado (aliases_component_template – veja abaixo), podemos fazê-lo da seguinte forma:</p><pre><code>PUT _component_template/aliases_component_template2
{
  "template": {
    "aliases": {
      "all_customers": {}
    }
  }
}</code></pre><p>O modelo de índice é o seguinte:</p><pre><code>PUT _index_template/composed_customers_template
{
  "index_patterns": [
    "*customers*"
  ],
  "priority": 200,
  "composed_of": [
    "settings_component_template",
    "aliases_component_template2"
  ]
}</code></pre><p>Você percebeu que o settings_component_template foi (re)utilizado em dois templates diferentes? Esse é o poder dos modelos de componentes.</p><h2>Prioridade do modelo de índice</h2><p>Existe a possibilidade de os desenvolvedores criarem vários modelos de índice sem analisar o estoque existente. É importante definir uma prioridade para cada um desses modelos, de forma que aquele com maior prioridade seja utilizado. Por exemplo, o modelo `my_orders_template_1` sobrescreve o modelo `my_orders_template_2` no seguinte trecho de código:</p><pre><code>PUT _index_template/my_orders_template_1
{
  "index_patterns": ["*orders"],
  "priority": 1000,
  "template": { ... }
}
PUT _index_template/my_orders_template2
{
  "index_patterns": ["*orders"],
  "priority": 300,
  "template": { ... }
}</code></pre><p>Quando você tem vários modelos que correspondem aos índices que estão sendo criados, o Elasticsearch aplica todas as configurações de todos os modelos correspondentes, mas sobrescreve qualquer configuração que tenha prioridade mais alta.</p><h2>Precedência dos modelos</h2><p>Por fim, você pode estar se perguntando sobre a precedência dos modelos – a configuração definida no modelo do componente substitui a definida no próprio modelo do índice principal? Ou vice-versa? Bem, existem algumas regras:</p><ul><li>Um índice criado com configurações explícitas tem precedência sobre tudo – isso significa que, se você criar um índice com configuração explícita, não espere que ela seja substituída pelos modelos.</li><li>Os modelos legados (modelos criados antes da versão 7.8) têm uma prioridade menor do que os modelos componíveis.</li></ul><h2>Resumo</h2><ul><li>Um índice contém mapeamentos, configurações e aliases: os mapeamentos definem o esquema dos campos, as configurações definem os parâmetros do índice, como o número de shards e réplicas, e os aliases fornecem nomes alternativos ao índice.</li><li>Os modelos permitem criar índices com configurações predefinidas. Ao atribuir um nome a um índice que corresponda ao padrão de índice definido em um modelo específico, esse índice será configurado automaticamente de acordo com o modelo.</li><li>O Elasticsearch introduziu modelos de índice componíveis na versão 7.8. Os modelos de índice combináveis permitem modularidade e versionamento dos modelos.</li><li>Os modelos componíveis consistem em nenhum ou mais modelos de componentes.</li><li>Um modelo de índice também pode ter sua própria configuração definida.</li><li>Um modelo de componente é um modelo reutilizável com configuração predefinida, assim como um modelo de índice composto.</li><li>No entanto, espera-se que os modelos de componentes façam parte de um modelo de índice; eles são inúteis se não forem &quot;compostos&quot; em um modelo de índice.</li><li>Os modelos de componentes não têm um padrão de índice definido neles – o que é mais um motivo pelo qual &quot;se espera&quot; que façam parte de um modelo de índice.</li><li>Cada um dos modelos tem uma prioridade – um número positivo. Quanto maior o número, maior a prioridade para a aplicação desse modelo.</li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Pesquisa no Elasticsearch por dois campos]]></title>
            <description><![CDATA[Explore técnicas de busca por dois campos, incluindo consultas de múltiplas correspondências, consultas booleanas e reforço de campos em tempo de consulta.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-search-by-two-fields</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-search-by-two-fields</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Wed, 30 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/d5d04bbcfc3925f48f3487ea4c7e0dd2205316d0-720x420.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>A busca em múltiplos campos no Elasticsearch é um requisito comum em muitas aplicações. Neste artigo, exploraremos técnicas avançadas para realizar buscas por dois campos, incluindo consultas com múltiplas correspondências, consultas booleanas e otimização de campos em tempo de consulta. Essas técnicas ajudarão você a criar resultados de pesquisa mais precisos e relevantes para seus usuários.</p><h2>Técnicas avançadas para realizar buscas por dois campos</h2><h3>1. Consulta com múltiplas correspondências</h3><p>Uma consulta de correspondência múltipla permite pesquisar uma única sequência de consulta em vários campos. Isso é útil quando você deseja encontrar documentos que contenham a string de consulta fornecida em qualquer um dos dois campos. Aqui está um exemplo de uma consulta de correspondência múltipla que busca o termo “exemplo” nos campos “título” ou “descrição”:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "example",
      "fields": ["title", "description"]
    }
  }
}</code></pre><h3>2. Consulta booleana</h3><p>Uma consulta booleana permite combinar várias consultas usando lógica booleana. Você pode usar a cláusula “should” para pesquisar documentos que correspondam à consulta em qualquer um dos dois campos. Aqui está um exemplo de uma consulta booleana que busca o termo “exemplo” nos campos “título” e “descrição”:</p><pre><code>{
  "query": {
    "bool": {
      "should": [
        {"match": {"title": "example"}},
        {"match": {"description": "example"}}
      ]
    }
  }
}</code></pre><h3>3. Reforço de campos em tempo de consulta</h3><p>Às vezes, você pode querer dar mais importância a um campo em detrimento de outro durante a pesquisa. Você pode conseguir isso aplicando um fator de reforço ao campo no momento da consulta. Um valor de reforço mais alto dá mais peso ao campo, tornando-o mais propenso a influenciar a pontuação final da pesquisa. Aqui está um exemplo de uma consulta com múltiplas correspondências e um fator de reforço aplicado ao campo &quot;título&quot;:</p><pre><code>{
  "query": {
    "multi_match": {
      "query": "example",
      "fields": ["title^3", "description"]
    }
  }
}</code></pre><p>Neste exemplo, o campo &quot;título&quot; tem um fator de reforço de 3, tornando-o três vezes mais importante que o campo &quot;descrição&quot; na determinação da pontuação de pesquisa.</p><h3>4. Combinando consultas com diferentes fatores de otimização</h3><p>Você também pode combinar várias consultas com diferentes fatores de reforço usando uma consulta booleana. Isso permite ajustar a importância de cada campo nos resultados da pesquisa. Aqui está um exemplo de uma consulta booleana com diferentes fatores de ponderação aplicados aos campos “título” e “descrição”:</p><pre><code>{
  "query": {
    "bool": {
      "should": [
        {"match": {"title": {"query": "example", "boost": 3}}},
        {"match": {"description": {"query": "example", "boost": 1}}}
      ]
    }
  }
}</code></pre><p>Neste exemplo, o campo &quot;título&quot; tem um fator de reforço de 3, enquanto o campo &quot;descrição&quot; tem um fator de reforço de 1.</p><h2>Conclusão</h2><p>A busca por dois campos no Elasticsearch pode ser realizada usando técnicas avançadas como consultas de correspondência múltipla, consultas booleanas e otimização de campos em tempo de consulta. Ao combinar essas técnicas, você pode criar resultados de pesquisa mais precisos e relevantes para seus usuários. Experimente diferentes combinações de consultas e fatores de otimização para encontrar a configuração de pesquisa ideal para o seu caso de uso específico.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[​​Construindo um fluxo de trabalho RAG usando LangGraph e Elasticsearch]]></title>
            <description><![CDATA[Aprenda como configurar e personalizar um modelo de agente de recuperação LangGraph com o Elasticsearch para criar um fluxo de trabalho RAG para recuperação de dados eficiente e respostas orientadas por IA.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/build-rag-workflow-langgraph-elasticsearch</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/build-rag-workflow-langgraph-elasticsearch</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Fri, 25 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/17b7e7b336f73e232375d1add582ae5f6c52a279-1440x840.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O <a href="https://github.com/langchain-ai/retrieval-agent-template">modelo de agente de recuperação LangGraph</a> é um projeto inicial desenvolvido pela LangChain para facilitar a criação de sistemas de perguntas e respostas baseados em recuperação usando o LangGraph no LangGraph Studio. Este modelo é pré-configurado para integração perfeita com o Elasticsearch, permitindo que os desenvolvedores criem rapidamente agentes que podem indexar e recuperar documentos de forma eficiente.</p><p>Este blog se concentra na execução e personalização do modelo do agente de recuperação LangChain usando o LangGraph Studio e o LangGraph CLI. O modelo fornece uma estrutura para a criação de aplicativos de geração aumentada de recuperação (RAG), aproveitando vários backends de recuperação, como o Elasticsearch.</p><p>Orientaremos você na configuração do ambiente e na execução eficiente do modelo com o Elastic, ao mesmo tempo em que personalizamos o fluxo do agente.</p><h2>Pré-requisitos</h2><p>Antes de prosseguir, certifique-se de ter o seguinte instalado:</p><ul><li>Implantação do Elasticsearch Cloud ou implantação do Elasticsearch no local (ou crie uma <a href="https://www.elastic.co/pt/cloud/cloud-trial-overview">avaliação gratuita de </a>14 dias no Elastic Cloud) - Versão 8.0.0 ou superior</li><li>Python 3.9+</li><li>Acesso a um provedor de LLM como <a href="https://cohere.com/">Cohere</a> (usado neste guia), <a href="https://openai.com/">OpenAI</a> ou <a href="https://www.anthropic.com/claude">Anthropic/Claude</a></li></ul><h2>Criando o aplicativo LangGraph</h2><h3>1. Instale o LangGraph CLI</h3><pre><code>pip install --upgrade "langgraph-cli[inmem]"</code></pre><h3>2. Crie o aplicativo LangGraph a partir do modelo de agente de recuperação</h3><pre><code>mkdir lg-agent-demo
cd lg-agent-demo
langgraph new lg-agent-demo </code></pre><p><em>Será apresentado um menu interativo que permitirá que você escolha entre uma lista de modelos disponíveis. </em>Selecione 4 para Agente de Recuperação e 1 para Python, conforme mostrado abaixo:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6a41a41f95c2477c67810adc7be46d91faf06878-1600x407.png" style="width: 100%;" /><ul><li><strong>Solução de problemas</strong>: se você encontrar o erro “urllib.error.URLError: &lt;erro urlopen [SSL: CERTIFICATE_VERIFY_FAILED] falha na verificação do certificado: não é possível obter o certificado do emissor local (_ssl.c:1000)&gt; “</li></ul><p>Execute o comando Instalar Certificado do Python para resolver o problema, conforme mostrado abaixo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/83ec238136c41738457299fd42c83aff32eb5b97-1407x75.png" style="width: 100%;" /><h3>3. Instalar dependências</h3><p>Na raiz do seu novo aplicativo LangGraph, crie um ambiente virtual e instale as dependências no modo <code>edit</code> para que suas alterações locais sejam usadas pelo servidor:</p><pre><code>#For Mac
python3 -m venv lg-demo
source lg-demo/bin/activate 
pip install -e .

#For Windows
python3 -m venv lg-demo
lg-demo\Scripts\activate 
pip install -e .</code></pre><h2>Configurando o ambiente</h2><h3>1. Crie um arquivo .environment arquivo</h3><p>O arquivo <code>.env</code> contém chaves de API e configurações para que o aplicativo possa se conectar ao LLM e ao provedor de recuperação escolhidos. Gere um novo arquivo <code>.env</code> duplicando a configuração de exemplo:</p><pre><code>cp .env.example .env</code></pre><h3>2. Configure o arquivo .env arquivo</h3><p>O arquivo <code>.env</code> vem com um conjunto de configurações padrão. Você pode atualizá-lo adicionando as chaves de API e os valores necessários com base na sua configuração. Quaisquer chaves que não sejam relevantes para seu caso de uso podem ser deixadas inalteradas ou removidas.</p><pre><code># To separate your traces from other applications
LANGSMITH_PROJECT=retrieval-agent

# LLM choice (set the API key for your selected provider):
ANTHROPIC_API_KEY=your_anthropic_api_key
FIREWORKS_API_KEY=your_fireworks_api_key
OPENAI_API_KEY=your_openai_api_key

# Retrieval provider (configure based on your chosen service):

## Elastic Cloud:
ELASTICSEARCH_URL=https://your_elastic_cloud_url
ELASTICSEARCH_API_KEY=your_elastic_api_key

## Elastic Local:
ELASTICSEARCH_URL=http://host.docker.internal:9200
ELASTICSEARCH_USER=elastic
ELASTICSEARCH_PASSWORD=changeme

## Pinecone:
PINECONE_API_KEY=your_pinecone_api_key
PINECONE_INDEX_NAME=your_pinecone_index_name

## MongoDB Atlas:
MONGODB_URI=your_mongodb_connection_string

# Cohere API key:
COHERE_API_KEY=your_cohere_api_key</code></pre><ul><li>Exemplo de arquivo <code>.env</code> (usando Elastic Cloud e Cohere)</li></ul><p>Abaixo está um exemplo de configuração <code>.env</code> para usar o <strong>Elastic Cloud</strong> como provedor de recuperação e o <strong>Cohere</strong> como LLM, conforme demonstrado neste blog:</p><pre><code># To separate your traces from other applications
LANGSMITH_PROJECT=retrieval-agent
#Retrieval Provider
# Elasticsearch configuration
ELASTICSEARCH_URL=elastic-url:443
ELASTICSEARCH_API_KEY=elastic_api_key
# Cohere API key
COHERE_API_KEY=cohere_api_key</code></pre><p><em>Observação: embora este guia utilize o Cohere para geração de respostas e incorporações, você pode usar outros provedores de LLM, como <strong>OpenAI</strong>, <strong>Claude</strong>ou até mesmo um modelo de LLM local, dependendo do seu caso de uso. Certifique-se de que cada chave que você pretende usar esteja presente e definida corretamente no </em>arquivo<em><code>.env</code>.</em></p><h3>3. Atualize o arquivo de configuração - configuration.py </h3><p>Depois de configurar seu arquivo <code>.env</code> com as chaves de API apropriadas, a próxima etapa é atualizar a configuração do modelo padrão do seu aplicativo. Atualizar a configuração garante que o sistema use os serviços e modelos que você especificou no seu arquivo <code>.env</code> .</p><p>Navegue até o arquivo de configuração:</p><pre><code> cd src/retrieval_graph</code></pre><p>O arquivo <code>configuration.py</code> contém as configurações de modelo padrão usadas pelo agente de recuperação para três tarefas principais:</p><ul><li><strong>Modelo de incorporação</strong> – converte documentos em representações vetoriais</li><li><strong>Modelo de consulta</strong> – processa a consulta do usuário em um vetor</li><li><strong>Modelo de resposta</strong> – gera a resposta final</li></ul><p>Por padrão, o código usa modelos do <strong>OpenAI</strong> (por exemplo, <code>openai/text-embedding-3-small</code>) e <strong>do Anthropic</strong> (por exemplo, <code>anthropic/claude-3-5-sonnet-20240620 and anthropic/claude-3-haiku-20240307</code>).<br/><br/>Neste blog, estamos mudando para o uso de modelos Cohere. Se você já estiver usando OpenAI ou Anthropic, nenhuma alteração será necessária.</p><h4>Exemplos de alterações (usando Cohere):</h4><p>Abra <code>configuration.py</code> e modifique os padrões do modelo conforme mostrado abaixo:</p><pre><code>…
 embedding_model: Annotated[
       str,
       {"__template_metadata__": {"kind": "embeddings"}},
   ] = field(
       default="cohere/embed-english-v3.0",
…
response_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
       default="cohere/command-r-08-2024",
…
query_model: Annotated[str, {"__template_metadata__": {"kind": "llm"}}] = field(
       default="cohere/command-r-08-2024",
       metadata={</code></pre><h2>Executando o agente de recuperação com a CLI do LangGraph</h2><h3>1. Inicie o servidor LangGraph</h3><pre><code>cd lg-agent-demo
langgraph dev</code></pre><p>Isso iniciará o servidor LangGraph API localmente. Se isso for executado com sucesso, você deverá ver algo como:</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e3c3344b24651067e2d0892d870feca505b3be35-1494x542.png" style="width: 100%;" /><p>URL da interface do usuário do Open Studio.</p><p>Há dois gráficos disponíveis:</p><ul><li><strong>Gráfico de recuperação</strong>: Recupera dados do Elasticsearch e responde à consulta usando um LLM (Language-Level Model).</li><li><strong>Gráfico do indexador</strong>: Indexa documentos no Elasticsearch e gera embeddings usando um LLM.</li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/99b579ca44b2d13f61da7e1de5886ea5b9a5e16f-1600x1009.png" style="max-width:100%; margin-bottom:1em;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/f54d182d2789b6a4d113b0c88063531ea0b6de5d-1600x1009.png" style="max-width:100%; margin-bottom:1em;" /><h3>2. Configurando o gráfico do indexador</h3><ul><li>Abra o gráfico do indexador.</li><li>Clique em Gerenciar assistentes.<ul><li>Clique em <strong>&#x27;Adicionar novo assistente</strong> &#x27;, insira os dados do usuário conforme especificado e, em seguida, feche a janela.</li></ul></li></ul><pre><code>{"user_id": "101"}</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/6f87958e40760ac9671639f4995962bcea090991-1600x1139.png" style="max-width:100%; margin-bottom:1em;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8f802f6b65b3f06d7d1a521c3f40385c6006413b-1422x1212.png" style="max-width:100%; margin-bottom:1em;" /><h3>3. Indexação de documentos de amostra</h3><ul><li>Indexe os seguintes documentos de exemplo, que representam um relatório trimestral hipotético para a organização NoveTech:</li></ul><pre><code>[
  {    "page_content": "NoveTech Solutions Q1 2025 Report - Revenue: $120.5M, Net Profit: $18.2M, EPS: $2.15. Strong AI software launch and $50M government contract secured."
  },
  {
    "page_content": "NoveTech Solutions Business Highlights - AI-driven analytics software gained 15% market share. Expansion into Southeast Asia with two new offices. Cloud security contract secured."
  },
  {
    "page_content": "NoveTech Solutions Financial Overview - Operating expenses at $85.3M, Gross Margin 29.3%. Stock price rose from $72.5 to $78.3. Market Cap reached $5.2B."
  },
  {
    "page_content": "NoveTech Solutions Challenges - Rising supply chain costs impacting hardware production. Regulatory delays slowing European expansion. Competitive pressure in cybersecurity sector."
  },
  {
    "page_content": "NoveTech Solutions Future Outlook - Expected revenue for Q2 2025: $135M. New AI chatbot and blockchain security platform launch planned. Expansion into Latin America."
  },
  {
    "page_content": "NoveTech Solutions Market Performance - Year-over-Year growth at 12.7%. Stock price increase reflects investor confidence. Cybersecurity and AI sectors remain competitive."
  },
  {
    "page_content": "NoveTech Solutions Strategic Moves - Investing in R&D to enhance AI-driven automation. Strengthening partnerships with enterprise cloud providers. Focusing on data privacy solutions."
  },
  {
    "page_content": "NoveTech Solutions CEO Statement - 'NoveTech Solutions continues to innovate in AI and cybersecurity. Our growth strategy remains strong, and we foresee steady expansion in the coming quarters.'"
  }
]</code></pre><p>Depois que os documentos forem indexados, você verá uma mensagem de exclusão no tópico, conforme mostrado abaixo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/fd3a1efd64cb54d54ea56ef5055249dd066d5708-1600x854.png" style="width: 100%;" /><h3>4. Executando o grafo de recuperação</h3><ul><li>Mude para o gráfico de recuperação.</li><li>Digite a seguinte consulta de pesquisa:</li></ul><pre><code>What was NovaTech Solutions total revenue in Q1 2025?</code></pre><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/eb4d8ddfe0effd7e1868fba921b8ef13f7baf27a-1600x755.png" style="width: 100%;" /><p>O sistema retornará documentos relevantes e fornecerá uma resposta exata com base nos dados indexados.</p><h2>Personalize o agente de recuperação.</h2><p>Para melhorar a experiência do usuário, introduzimos uma etapa de personalização no grafo de recuperação para prever as próximas três perguntas que um usuário poderá fazer. Essa previsão se baseia em:</p><ul><li>Contexto dos documentos recuperados</li><li>Interações anteriores do usuário</li><li>Última consulta do usuário</li></ul><p>As seguintes alterações de código são necessárias para implementar o recurso de Previsão de Consulta:</p><h3>1. Atualize o arquivo graph.py</h3><ul><li>Adicione a função <code>predict_query</code> :</li></ul><pre><code>async def predict_query(
   state: State, *, config: RunnableConfig
) -> dict[str, list[BaseMessage]]:
   logger.info(f"predict_query predict_querypredict_query predict_query predict_query predict_query")  # Log the query

   configuration = Configuration.from_runnable_config(config)
   prompt = ChatPromptTemplate.from_messages(
       [
           ("system", configuration.predict_next_question_prompt),
           ("placeholder", "{messages}"),
       ]
   )
   model = load_chat_model(configuration.response_model)
   user_query = state.queries[-1] if state.queries else "No prior query available"
   logger.info(f"user_query: {user_query}")
   logger.info(f"statemessage: {state.messages}")
   #human_messages = [msg for msg in state.message if isinstance(msg, HumanMessage)]

   message_value = await prompt.ainvoke(
       {
           "messages": state.messages,
           "user_query": user_query,  # Use the most recent query as primary input
           "system_time": datetime.now(tz=timezone.utc).isoformat(),
       },
       config,
   )

   next_question = await model.ainvoke(message_value, config)
   return {"next_question": [next_question]}</code></pre><ul><li>Modifique a função <code>respond</code> para retornar o objeto <strong><code>response</code></strong> , em vez da mensagem:</li></ul><pre><code>async def respond(
   state: State, *, config: RunnableConfig
) -> dict[str, list[BaseMessage]]:
   """Call the LLM powering our "agent"."""
   configuration = Configuration.from_runnable_config(config)
   # Feel free to customize the prompt, model, and other logic!
   prompt = ChatPromptTemplate.from_messages(
       [
           ("system", configuration.response_system_prompt),
           ("placeholder", "{messages}"),
       ]
   )
   model = load_chat_model(configuration.response_model)

   retrieved_docs = format_docs(state.retrieved_docs)
   message_value = await prompt.ainvoke(
       {
           "messages": state.messages,
           "retrieved_docs": retrieved_docs,
           "system_time": datetime.now(tz=timezone.utc).isoformat(),
       },
       config,
   )
   response = await model.ainvoke(message_value, config)
   # We return a list, because this will get added to the existing list
   return {"response": [response]}</code></pre><ul><li>Atualizar estrutura do gráfico para adicionar novo nó e aresta para predict_query:</li></ul><pre><code>builder.add_node(generate_query)
builder.add_node(retrieve)
builder.add_node(respond)
builder.add_node(predict_query)
builder.add_edge("__start__", "generate_query")
builder.add_edge("generate_query", "retrieve")
builder.add_edge("retrieve", "respond")
builder.add_edge("respond", "predict_query")</code></pre><h3>2. Atualize o arquivo prompts.py</h3><ul><li>Crie um prompt para previsão de guery em <code>prompts.py</code>:</li></ul><pre><code>PREDICT_NEXT_QUESTION_PROMPT = """Given the user query and the retrieved documents, suggest the most likely next question the user might ask.

**Context:**
- Previous Queries:
{previous_queries}

- Latest User Query: {user_query}

- Retrieved Documents:
{retrieved_docs}

**Guidelines:**
1. Do not suggest a question that has already been asked in previous queries.
2. Consider the retrieved documents when predicting the next logical question.
3. If the user's query is already fully answered, suggest a relevant follow-up question.
4. Keep the suggested question natural and conversational.
5. Suggest at least 3 question

System time: {system_time}"""</code></pre><h3>3. Atualize o arquivo configuration.py</h3><ul><li>Adicionar <code>predict_next_question_prompt</code>:</li></ul><pre><code>predict_next_question_prompt: str = field(
       default=prompts.PREDICT_NEXT_QUESTION_PROMPT,
       metadata={"description": "The system prompt used for generating responses."},
   )</code></pre><h3>4. Atualize o arquivo state.py</h3><ul><li>Adicione os seguintes atributos:</li></ul><pre><code>response: Annotated[Sequence[AnyMessage], add_messages]
next_question : Annotated[Sequence[AnyMessage], add_messages]</code></pre><h3>5. Execute novamente o grafo de recuperação.</h3><ul><li>Digite a seguinte consulta de pesquisa novamente:</li></ul><pre><code>What was NovaTech Solutions total revenue in Q1 2025?</code></pre><p>O sistema processará a entrada e preverá três perguntas relacionadas que os usuários podem fazer, conforme mostrado abaixo.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/70e855a2e4edc0ba5a147588df0de30eb081d053-1600x777.png" style="width: 100%;" /><h2>Conclusão</h2><p>A integração do modelo do Retrieval Agent no LangGraph Studio e na CLI oferece vários benefícios importantes:</p><ul><li><strong>Desenvolvimento acelerado</strong>: o modelo e as ferramentas de visualização simplificam a criação e a depuração de fluxos de trabalho de recuperação, reduzindo o tempo de desenvolvimento.</li><li><strong>Implantação perfeita</strong>: o suporte integrado para APIs e o dimensionamento automático garantem uma implantação tranquila em todos os ambientes.</li><li><strong>Atualizações fáceis:</strong> modificar fluxos de trabalho, adicionar novas funcionalidades e integrar nós adicionais é simples, facilitando o dimensionamento e o aprimoramento do processo de recuperação.</li><li><strong>Memória persistente</strong>: o sistema retém os estados e o conhecimento dos agentes, melhorando a consistência e a confiabilidade.</li><li><strong>Modelagem de fluxo de trabalho flexível</strong>: os desenvolvedores podem personalizar a lógica de recuperação e as regras de comunicação para casos de uso específicos.</li><li><strong>Interação e depuração em tempo real</strong>: a capacidade de interagir com agentes em execução permite testes eficientes e resolução de problemas.</li></ul><p>Ao aproveitar esses recursos, as organizações podem criar sistemas de recuperação poderosos, eficientes e escaláveis que melhoram a acessibilidade dos dados e a experiência do usuário.</p><p>O código-fonte completo deste projeto está disponível no <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/langraph-retrieval-agent-template-demo">GitHub</a>.</p>]]></content:encoded>
        </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>
            <link>https://www.elastic.co/search-labs/pt/blog/bbq-implementation-into-use-case</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/bbq-implementation-into-use-case</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <dc:creator><![CDATA[Sachin Frayne]]></dc:creator>
            <pubDate>Wed, 23 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/66842055367cdd795532b01c167f2a4b03dc65e3-1200x628.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <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><blockquote>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.</blockquote><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>Í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.<ul><li>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.</li></ul></li></ul><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/590f3b3c72a76023a38a0436cd9ff90a9f80e936-1964x1262.png" style="width: 100%;" /><ul><li>O HNSW é fundamentalmente limitado em velocidade pela velocidade de leitura de dados da memória ou, no pior caso, do armazenamento.<ul><li>O ideal é que você consiga carregar todos os seus vetores armazenados na memória.</li></ul></li><li>Os modelos de incorporação geralmente produzem vetores com precisão float32, 4 bytes por número de ponto flutuante.</li><li>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.</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>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.</li><li>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.)</li><li>Se estiver usando o Serverless, você precisará selecionar uma instância otimizada para vetores.</li><li>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:<ul><li><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></li><li><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></li></ul></li></ul><h2>Implementação de Quantização Binária Aprimorada (BBQ)</h2><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/4d388078495566f0527e931e0c2e38facdce83c6-1503x748.png" style="width: 100%;" /><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><pre><code>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
    }
  }
}</code></pre><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><pre><code>POST _inference/text_embedding/my_e5_model
{
  "input": "my awesome piece of text"
}</code></pre><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><pre><code>POST _ml/trained_models/.multilingual-e5-small/deployment/_start</code></pre><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><pre><code>PUT bbq-my-byte-quantized-index
{
  "mappings": {
    "properties": {
      "my_field": {
        "type": "text"
      },
      "my_vector": {
        "type": "dense_vector",
        "dims": 384,
        "index_options": {
          "type": "bbq_hnsw"
        }
      }
    }
  }
}</code></pre><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><pre><code>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"
      }
    }
  ]
}</code></pre><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><pre><code>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"
  ]
}</code></pre><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><pre><code>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"
  ]
}</code></pre><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><pre><code>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"
  ]
}</code></pre><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>
            <table style="border-collapse: collapse; margin: 20px 0;">
              <tbody>
                
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">Vetores (V)</td><td style="border: 1px solid #ccc; padding: 4px;">Dimensões (D)</td><td style="border: 1px solid #ccc; padding: 4px;">cru (V x D x 4)</td><td style="border: 1px solid #ccc; padding: 4px;">int8 (V x (D x 1 + 4))</td><td style="border: 1px solid #ccc; padding: 4px;">int4 (V x (D x 0,5 + 4))</td><td style="border: 1px solid #ccc; padding: 4px;">churrasco (V x (D x 0,125 + 4))</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">10.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">14,31 GB</td><td style="border: 1px solid #ccc; padding: 4px;">3,61 GB</td><td style="border: 1px solid #ccc; padding: 4px;">1,83 GB</td><td style="border: 1px solid #ccc; padding: 4px;">0,58 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">50.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">71,53 GB</td><td style="border: 1px solid #ccc; padding: 4px;">18,07 GB</td><td style="border: 1px solid #ccc; padding: 4px;">9,13 GB</td><td style="border: 1px solid #ccc; padding: 4px;">2,89 GB</td>
                    </tr>
                    <tr>
                      <td style="border: 1px solid #ccc; padding: 4px;">100.000.000</td><td style="border: 1px solid #ccc; padding: 4px;">384</td><td style="border: 1px solid #ccc; padding: 4px;">143,05 GB</td><td style="border: 1px solid #ccc; padding: 4px;">36,14 GB</td><td style="border: 1px solid #ccc; padding: 4px;">18,25 GB</td><td style="border: 1px solid #ccc; padding: 4px;">5,77 GB</td>
                    </tr>
              </tbody>
            </table>
          <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><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></li><li><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></li><li><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></li><li><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></li></ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Utilização do tamanho do heap do Elasticsearch e coleta de lixo da JVM]]></title>
            <description><![CDATA[Explorando o uso do tamanho do heap do Elasticsearch e a coleta de lixo da JVM, incluindo as melhores práticas e como resolver problemas quando o uso da memória heap está muito alto ou quando o desempenho da JVM não é ideal.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-heap-size-jvm-garbage-collection</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-heap-size-jvm-garbage-collection</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/b162c28623b9070fd1980bcd891b9dd1e868f2f0-720x421.jpg?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>O tamanho do heap é a quantidade de RAM alocada para a Máquina Virtual Java de um nó do Elasticsearch.</p><p>A partir da versão 7.11, o Elasticsearch define automaticamente, por padrão, o tamanho do heap da JVM com base nas funções e na memória total de um nó. Para a maioria dos ambientes de produção, recomenda-se o uso do dimensionamento padrão. No entanto, se você quiser definir manualmente o tamanho do heap da JVM, como regra geral, você deve definir -Xms e -Xmx com o MESMO valor, que deve ser 50% da sua RAM total disponível, sujeito a um máximo de (aproximadamente) 31 GB.</p><p>Um tamanho de heap maior dará ao seu nó mais memória para operações de indexação e pesquisa. No entanto, seu nó também requer memória para cache, portanto, usar 50% mantém um equilíbrio saudável entre os dois. Pelo mesmo motivo, em produção, você deve evitar usar outros processos que consomem muita memória no mesmo nó que o Elasticsearch.</p><p>Normalmente, a utilização da memória heap seguirá um padrão em dente de serra, oscilando entre cerca de 30 e 70% da capacidade máxima da heap. Isso ocorre porque a JVM aumenta gradualmente a porcentagem de uso do heap até que o processo de coleta de lixo libere memória novamente. O uso elevado da memória heap ocorre quando o processo de coleta de lixo não consegue acompanhar. Um indicador de alto uso da memória heap é quando a coleta de lixo é incapaz de reduzir o uso da memória heap para cerca de 30%.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/0a17a67cc589a3c1fbf9e918eadc119df7bd7619-858x278.png" style="width: 100%;" /><p>Na imagem acima, você pode ver um padrão típico de dente de serra no heap da JVM.</p><p>Você também verá que existem dois tipos de coleta de lixo: coleta de lixo jovem e coleta de lixo antiga.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/8df5c24c4894404de4617be7a13683c9027d607d-875x281.png" style="width: 100%;" /><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/e01eb2537310b052580411153b8eddc187d97687-890x264.png" style="width: 100%;" /><p>Em uma JVM saudável, a coleta de lixo deve idealmente atender às seguintes condições:</p><ul><li>O GC jovem é processado rapidamente (em 50 ms).</li><li>O coletor de lixo jovem não é executado com frequência (cerca de 10 segundos).</li><li>O GC antigo é processado rapidamente (em menos de 1 segundo).</li><li>A coleta de lixo antiga não é executada com frequência (uma vez a cada 10 minutos ou mais).</li></ul><h3><strong>Como resolver problemas quando o uso da memória heap está muito alto ou quando o desempenho da JVM não está ideal</strong></h3><p>Existem diversos motivos pelos quais o uso da memória heap pode aumentar:</p><h4><strong>Sobrefragmentação</strong></h4><p>Consulte o documento sobre sobreparticionamento <a href="https://www.elastic.co/docs/deploy-manage/production-guidance/optimize-performance/size-shards#sizing-shard-guidelines">aqui</a>.</p><h4><strong>Tamanhos de agregação grandes</strong></h4><p>Para evitar tamanhos de agregação muito grandes, mantenha o número de buckets de agregação (tamanho) em suas consultas no mínimo.</p><pre><code>GET /_search
{
   "aggs" : {
       "products" : {
           "terms" : {
               "field" : "product",
               "size" : 5
                          }
       }
   }
}</code></pre><p>Você pode usar o registro de consultas lentas (logs lentos) e implementá-lo em um índice específico usando o seguinte.</p><pre><code>PUT /my_index/_settings
{
   "index.search.slowlog.threshold.query.warn": "10s",
   "index.search.slowlog.threshold.query.info": "5s",
   "index.search.slowlog.threshold.query.debug": "2s",
   "index.search.slowlog.threshold.query.trace": "500ms",
   "index.search.slowlog.threshold.fetch.warn": "1s",
   "index.search.slowlog.threshold.fetch.info": "800ms",
   "index.search.slowlog.threshold.fetch.debug": "500ms",
   "index.search.slowlog.threshold.fetch.trace": "200ms",
   "index.search.slowlog.level": "info"
}</code></pre><p>Consultas que demoram muito para retornar resultados provavelmente são as que consomem mais recursos.</p><h4><strong>Tamanho excessivo do índice de volume</strong></h4><p>Se você estiver enviando solicitações grandes, isso pode ser a causa de um alto consumo de memória heap. Tente reduzir o tamanho das solicitações de indexação em lote.</p><h4><strong>Problemas de mapeamento</strong></h4><p>Em particular, se você usar “fielddata: true”, isso pode consumir grande parte da memória heap da sua JVM.</p><h4><strong>Tamanho do heap configurado incorretamente</strong></h4><p>O tamanho do heap pode ser definido manualmente por:</p><p>Definindo a variável de ambiente:</p><pre><code>ES_JAVA_OPTS="-Xms2g -Xmx2g"</code></pre><p>Edite o arquivo jvm.options no diretório de configuração do Elasticsearch:</p><pre><code>-Xms2g
-Xmx2g</code></pre><p>A configuração da variável de ambiente tem prioridade sobre a configuração do arquivo.</p><p>É necessário reiniciar o nó para que a configuração seja considerada.</p><h4><strong>A nova proporção da JVM foi configurada incorretamente.</strong></h4><p>Geralmente NÃO é necessário configurar isso, pois o Elasticsearch define esse valor por padrão. Este parâmetro define a proporção de espaço disponível para objetos de “nova geração” e de “geração antiga” na JVM.</p><p>Se você perceber que as coletas de lixo antigas estão se tornando muito frequentes, pode tentar definir esse valor especificamente no arquivo jvm.options no diretório de configuração do Elasticsearch.</p><pre><code>-XX:NewRatio=3</code></pre><h3><strong>Quais são as melhores práticas para gerenciar o uso do tamanho do heap e a coleta de lixo da JVM em um cluster Elasticsearch de grande porte?</strong></h3><p>As melhores práticas para gerenciar o uso do tamanho do heap e a coleta de lixo da JVM em um cluster Elasticsearch de grande porte consistem em garantir que o tamanho do heap seja definido para, no máximo, 50% da RAM disponível e que as configurações de coleta de lixo da JVM sejam otimizadas para o caso de uso específico. É importante monitorar o tamanho do heap e as métricas de coleta de lixo para garantir que o cluster esteja funcionando de forma otimizada. Especificamente, é importante monitorar o tamanho do heap da JVM, o tempo de coleta de lixo e as pausas na coleta de lixo. Além disso, é importante monitorar o número de ciclos de coleta de lixo e o tempo gasto nessa atividade. Ao monitorar essas métricas, é possível identificar quaisquer problemas potenciais com o tamanho do heap ou com as configurações de coleta de lixo e tomar medidas corretivas, se necessário.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como aumentar o número de shards primários no Elasticsearch]]></title>
            <description><![CDATA[Aprenda como aumentar o número de shards principais no Elasticsearch usando as APIs split e reindex para ter o redimensionamento ideal de shards.]]></description>
            <link>https://www.elastic.co/search-labs/pt/blog/elasticsearch-increase-primary-shard-count</link>
            <guid isPermaLink="true">https://www.elastic.co/search-labs/pt/blog/elasticsearch-increase-primary-shard-count</guid>
            <category><![CDATA[elasticsearch-labs]]></category>
            <pubDate>Thu, 17 Apr 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/7034b76019a0cba52c25eda29fceb18afc96ed0b-720x420.png?fm=webp&amp;w=1440&amp;h=754&amp;q=90&amp;fit=crop" length="0" type="image/webp"/>
            <content:encoded><![CDATA[<p>Não é possível aumentar o número de shards primários de um índice existente, o que significa que um índice precisa ser recriado se você quiser aumentar a quantidade de shards primários. Geralmente, existem dois métodos utilizados nessas situações: a API _reindex e a API _split.</p><p>A API _split costuma ser um método mais rápido do que a API _reindex. <strong>A indexação</strong> <strong>deve ser interrompida</strong> antes de ambas as operações; caso contrário, as contagens de documentos em source_index e target_index serão diferentes.</p><img src="https://cdn.sanity.io/images/me0ej585/search-labs-import-testing/aa0ae010c2f5691ca00440fb453ed6b47bacd24f-1200x628.png" style="width: 100%;" /><h2>Método 1 – usando a API dividida</h2><p>A API de divisão é usada para criar um novo índice com o número desejado de shards primários, copiando as configurações e mapeando um índice existente. O número desejado de fragmentos primários pode ser definido durante a criação. As seguintes configurações devem ser verificadas antes de implementar a API dividida:</p><ol><li>O índice de origem deve ser somente leitura. Isso significa que o processo de indexação precisa ser interrompido.</li><li>O número de shards primários no índice de destino deve ser um múltiplo do número de shards primários no índice de origem. Por exemplo, se o índice de origem tiver 5 shards primários, o número de shards primários do índice de destino pode ser definido como 10, 15, 20 e assim por diante.</li></ol><p>Observação: Se apenas o número do fragmento primário precisar ser alterado, a API de divisão é preferível, pois é muito mais rápida do que a API de reindexação.</p><h3>Implementando a API dividida</h3><p>Criar um índice de teste:</p><pre><code>POST test_split_source/_doc
{
  "test": "test"
}</code></pre><p>O índice de origem deve ser somente leitura para poder ser dividido:</p><pre><code>PUT test_split_source/_settings
{
  "index.blocks.write": true
}</code></pre><p>As configurações e os mapeamentos serão copiados automaticamente do índice de origem:</p><pre><code>POST /test_split_source/_split/test_split_target
{
  "settings": {
    "index.number_of_shards": 3
  }
}</code></pre><p>Você pode verificar o progresso com:</p><pre><code>GET _cat/recovery/test_split_target?v&h=index,shard,time,stage,files_percent,files_total</code></pre><p>Como as configurações e os mapeamentos são copiados dos índices de origem, o índice de destino é somente leitura. Vamos habilitar a operação de escrita para o índice de destino:</p><pre><code>PUT test_split_target/_settings
{
    "index.blocks.write": null
}</code></pre><p>Verifique a contagem de documentos (docs.count) nos índices de origem e destino antes de excluir o índice original:</p><pre><code>GET _cat/indices/test_split*?v&h=index,pri,rep,docs.count</code></pre><p>O nome do índice e o nome do alias não podem ser iguais. Você precisa excluir o índice de origem e adicionar o nome do índice de origem como um alias para o índice de destino:</p><pre><code>DELETE test_split_source
PUT /test_split_target/_alias/test_split_source</code></pre><p>Após adicionar o alias <strong>test_split_source</strong> ao índice <strong>test_split_target</strong> , você deve testá-lo com:</p><pre><code>GET test_split_source
POST test_split_source/_doc
{
  "test": "test"
}</code></pre><h2>Método 2 – usando a API de reindexação</h2><p>Ao criar um novo índice com a API Reindex, é possível definir qualquer número de shards primários. Após a criação de um novo índice com o número desejado de shards primários, todos os dados do índice de origem podem ser reindexados para esse novo índice.</p><p>Além dos recursos de API dividida, os dados podem ser manipulados usando o ingest_pipeline no AP de reindexação. Com o pipeline de ingestão, somente os campos especificados que correspondem ao filtro serão indexados no índice de destino usando a consulta. O conteúdo dos dados pode ser alterado usando um script simples, e vários índices podem ser mesclados em um único índice.</p><h3>Implementando a API de reindexação</h3><p>Criar um reindexador de teste:</p><pre><code>POST test_reindex_source/_doc
{
    "test": "test"
}</code></pre><p>Copie as configurações e os mapeamentos do índice de origem:</p><pre><code>GET test_reindex_source</code></pre><p>Crie um índice de destino com configurações, mapeamentos e a quantidade desejada de fragmentos (shards):</p><pre><code>PUT test_reindex_target
{
  "mappings" : {},
  "settings": {
    "number_of_shards": 10,
    "number_of_replicas": 0,
    "refresh_interval": -1
  }
}</code></pre><p>*Nota: definir number_of_replicas: 0 e refresh_interval: -1 aumentará a velocidade de reindexação.</p><p>Inicie o processo de reindexação. Definir requests_per_second=-1 e slices=auto ajustará a velocidade de reindexação.</p><pre><code>POST _reindex?requests_per_second=-1&slices=auto&wait_for_completion=false
{
  "source": {
    "index": "test_reindex_source"
  },
  "dest": {
    "index": "test_reindex_target"
  }
}</code></pre><p>Você verá o task_id ao executar a API de reindexação. Copie isso e verifique com a API _tasks:</p><pre><code>GET _tasks/<task_id></code></pre><p>Atualize as configurações após a conclusão da reindexação:</p><pre><code>PUT test_reindex_target/_settings
{
  "number_of_replicas": 1,
  "refresh_interval": "1s"
}</code></pre><p>Antes de excluir o índice original, verifique o número de documentos (docs.count) nos índices de origem e destino; eles devem ser iguais.</p><pre><code>GET _cat/indices/test_reindex_*?v&h=index,pri,rep,docs.count</code></pre><p>O nome do índice e o nome do alias não podem ser iguais. Exclua o índice de origem e adicione o nome do índice de origem como um alias para o índice de destino:</p><pre><code>DELETE test_reindex_source
PUT /test_reindex_target/_alias/test_reindex_source</code></pre><p>Após adicionar o alias test_split_source ao índice test_split_target, teste-o usando:</p><pre><code>GET test_reindex_source</code></pre><h2>Resumo</h2><p>Se você deseja aumentar o número de shards primários de um índice existente, precisa recriar as configurações e os mapeamentos para um novo índice. Existem dois métodos principais para fazer isso: a API de reindexação e a API de divisão. A indexação ativa deve ser interrompida antes de usar qualquer um dos métodos.</p>]]></content:encoded>
        </item>
    </channel>
</rss>