<?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[Florian Bernd - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Florian Bernd - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/pt/search-labs/author/florian-bernd</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/author/florian-bernd</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/author/florian-bernd.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 16:08:43 GMT</lastBuildDate>
  <item>
    <title><![CDATA[LINQ para Elasticsearch ES|QL: escreva Consultas em C# e Consulte o Elasticsearch]]></title>
    <description><![CDATA[Explorando o novo provedor LINQ para Elasticsearch ES|QL no cliente Elasticsearch .NET, que permite escrever código C# automaticamente traduzido para consultas ES|QL.]]></description>
    <content:encoded><![CDATA[<p>A partir das <strong>versões 9.3.4</strong> e <strong>8.19.18</strong>, o cliente Elasticsearch .NET inclui um provedor de <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/">Consulta Integrada em Linguagem (LINQ) </a>que traduz expressões LINQ em C# para a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">Linguagem de Consulta Elasticsearch (ES|QL)</a> em tempo de execução. Em vez de escrever manualmente as strings ES|QL, você compõe consultas usando <code>Where</code>, <code>Select</code>, <code>OrderBy</code>, <code>GroupBy</code> e outros operadores padrão. O provedor cuida da tradução, parametrização e desserialização dos resultados, inclusive o streaming por linha que mantém o uso da memória constante, independentemente do tamanho do conjunto de resultados.</p><h2>Sua primeira consulta</h2><p>Comece definindo um objeto CLR simples (POCO) que mapeia para o seu índice Elasticsearch. Os nomes das propriedades são resolvidos para nomes de coluna ES|QL via atributos <code>System.Text.Json</code> padrão, como <code>[JsonPropertyName]</code>, ou via <code>JsonNamingPolicy</code> configurado. As mesmas regras <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/source-serialization">de serialização de origem</a> que se aplicam ao restante do cliente também se aplicam aqui.</p>using System.Text.Json.Serialization;

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

    public string Name { get; set; }

    public string Brand { get; set; }

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

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

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

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

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

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

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

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

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

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

await asyncQuery.WaitForCompletionAsync();
await foreach (var product in asyncQuery.AsAsyncEnumerable())
    Console.WriteLine(product.Name);<p>Consultas assíncronas do lado do servidor são úteis principalmente em consultas analíticas de longa duração/processamento de grandes conjuntos de dados que podem exceder os tempos-limite típicos, ou em ambientes sensíveis a tempo-limite com balanceadores de carga, gateways de API ou proxies que impõem tempos-limite de HTTP rigorosos. Consultas assíncronas evitam quedas de conexão ao separar o envio da obtenção dos resultados.</p><h2>Para começar</h2><p>LINQ to ES|QL está disponível a partir de:</p><ul><li><p><strong>Elastic.Clients.Elasticsearch v9.3.4</strong> (9.x branch)</p></li><li><p><strong>Elastic.Clients.Elasticsearch v8.19.18</strong> (8.x branch)</p></li></ul><p>Instale do NuGet:</p><p><code>dotnet add package Elastic.Clients.Elasticsearch</code></p><p>Os pontos de entrada estão em <code>client.Esql</code>:</p><p>Método</p><p>Returns</p><p>Caso de uso</p><p>Query&lt;T&gt;(...)</p><p>IEnumerable&lt;T&gt;</p><p>Execução síncrona</p><p>QueryAsync&lt;T&gt;(...)</p><p>IAsyncEnumerable&lt;T&gt;</p><p>Streaming assíncrono</p><p>CreateQuery&lt;T&gt;()</p><p>IEsqlQueryable&lt;T&gt;</p><p>Composição avançada e inspeção</p><p>SubmitAsyncQueryAsync&lt;T&gt;(...)</p><p>EsqlAsyncQuery&lt;T&gt;</p><p>Consultas de longa duração no servidor</p><p><a href="https://www.elastic.co/docs/reference/elasticsearch/clients/dotnet/linq-to-esql">Para obter a referência completa de recursos, incluindo opções de consulta, acesso a vários campos, objetos aninhados e tratamento de campos de vários valores, consulte a documentação do LINQ to ES|QL.</a></p><h2>Conclusão</h2><p>Do LINQ para ES|QL traz toda a expressividade do C# LINQ para a linguagem de consulta ES|QL do Elasticsearch, para que você escreva consultas componíveis e com tipagem forte sem precisar criar manualmente as strings de consulta. Com captura automática de parâmetros, materialização em streaming e arquitetura de pacotes em camadas que se adapta de traduções independentes ao cliente completo do Elasticsearch, ele se integra naturalmente a aplicações .NET de qualquer tamanho. Instale o cliente mais recente, direcione suas expressões LINQ para um índice e deixe o provedor cuidar do resto.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/linq-esql-c-elasticsearch-net-client</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Martijn Laarman]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa35fbcbbf4959f/6a1705b9dc55de19a4e00d07/e54132e915217063e9ed0ec45059c6cfc38e31dd-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como usar o conector Elasticsearch Vector Store para o Microsoft Semantic Kernel no desenvolvimento de agentes de IA]]></title>
    <description><![CDATA[O Microsoft Semantic Kernel é um kit de desenvolvimento leve e de código aberto que permite criar facilmente agentes de IA e integrar os modelos de IA mais recentes em seu código C#, Python ou Java. Com o lançamento do Semantic Kernel Elasticsearch Vector Store Connector, os desenvolvedores que usam o Semantic Kernel para criar agentes de IA agora podem integrar o Elasticsearch como um armazenamento vetorial escalável de nível empresarial, continuando a usar as abstrações do Semantic Kernel.]]></description>
    <content:encoded><![CDATA[<p>Em colaboração com a equipe <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> , estamos anunciando a disponibilidade do <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a> para usuários <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> (.NET). O Semantic Kernel simplifica a criação de agentes de IA de nível empresarial, incluindo a capacidade de aprimorar grandes modelos de linguagem (LLMs) com respostas mais relevantes e orientadas por dados de um Repositório de Vetores. O Semantic Kernel fornece uma camada de abstração perfeita para interagir com armazenamentos vetoriais como o Elasticsearch, oferecendo recursos essenciais como criar, listar e excluir coleções de registros, além de carregar, recuperar e excluir registros individuais.</p><p><a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">O conector Semantic Kernel Elasticsearch Vector Store, pronto para uso,</a> oferece suporte às <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">abstrações de armazenamento vetorial</a> do Semantic Kernel, o que facilita muito para os desenvolvedores a integração do Elasticsearch como um armazenamento vetorial durante a criação de agentes de IA.</p><p>O Elasticsearch possui uma base sólida na comunidade de código aberto e adotou recentemente a <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">licença AGPL</a>. Em conjunto com o Microsoft Semantic Kernel de código aberto, essas ferramentas oferecem uma solução poderosa e pronta para uso empresarial. Você pode começar localmente executando o Elasticsearch em poucos minutos executando este comando <code>curl -fsSL https://elastic.co/start-local | sh </code>(consulte <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> para obter detalhes) e migrar para versões <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">hospedadas na nuvem</a> ou <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">auto-hospedadas</a> à medida que coloca seus agentes de IA em produção.</p><p>Neste blog, veremos como usar <a href="https://github.com/elastic/semantic-kernel-net/">o conector Elasticsearch Vector Store do Semantic Kernel</a> ao utilizar o Semantic Kernel. Uma versão em Python do conector será disponibilizada futuramente.</p><h2>Cenário geral: Construindo um aplicativo RAG com Semantic Kernel e Elasticsearch.</h2><p>Na seção seguinte, analisaremos um exemplo. Em linhas gerais, estamos construindo um aplicativo RAG (Retrieval Augmented Generation) que recebe uma pergunta do usuário como entrada e retorna uma resposta. Usaremos o Azure OpenAI (também é possível usar <a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">um LLM local</a> ) como LLM, o Elasticsearch como repositório de vetores e o Semantic Kernel (.NET) como framework para integrar todos os componentes.</p><p>Se você não estiver familiarizado com arquiteturas RAG, pode ter uma breve introdução com este artigo: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a>.</p><p>A resposta é gerada pelo LLM, que é alimentado com contexto relevante para a pergunta, obtido do armazenamento de vetores do Elasticsearch. A resposta também inclui a fonte que foi usada como contexto pelo LLM.</p><h3>Exemplo RAG</h3><p>Neste exemplo específico, criamos um aplicativo que permite aos usuários fazer perguntas sobre hotéis armazenados em um banco de dados interno de hotéis. O usuário poderia, por exemplo... Pesquise um hotel específico com base em diferentes critérios ou solicite uma lista de hotéis.</p><p>Para o banco de dados de exemplo, geramos uma <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">lista de hotéis</a> contendo 100 entradas. O tamanho da amostra é intencionalmente pequeno para permitir que você experimente a demonstração do conector da maneira mais fácil possível. Em uma aplicação do mundo real, o conector Elasticsearch mostraria suas vantagens sobre outras opções, como a implementação de armazenamento vetorial `InMemory`, especialmente ao trabalhar com quantidades extremamente grandes de dados.</p><p>A aplicação de demonstração completa pode ser encontrada no <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">repositório</a> do conector de armazenamento vetorial do Elasticsearch.</p><p>Vamos começar adicionando os pacotes NuGet necessários e usando as diretivas ao nosso projeto:</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>A resposta correta refere-se à seguinte entrada em nosso arquivo hotels.csv.</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>Este exemplo demonstra muito bem como a utilização do Microsoft Semantic Kernel permite uma redução significativa da complexidade através das suas abstrações bem concebidas, além de proporcionar um elevado nível de flexibilidade. Ao alterar uma única linha de código, por exemplo, o armazenamento de vetores ou os serviços de IA utilizados podem ser substituídos sem a necessidade de refatorar qualquer outra parte do código.</p><p>Ao mesmo tempo, a estrutura fornece um enorme conjunto de funcionalidades de alto nível, como a função `InvokePrompt`, ou o sistema de plugins de modelo ou de pesquisa.</p><p>A aplicação de demonstração completa pode ser encontrada no repositório do conector de armazenamento vetorial do Elasticsearch.</p><h2>O que mais é possível fazer com o Elasticsearch?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Novo mapeamento semantic_text do Elasticsearch: Simplificando a busca semântica</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Reclassificação semântica no Elasticsearch com retrievers</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Técnicas avançadas de RAG, parte 1: Processamento de dados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Técnicas avançadas de RAG, parte 2: Consultas e testes</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Construindo o RAG com Llama 3 de código aberto e Elastic</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Um tutorial sobre como construir um agente local usando LangGraph, LLaMA3 e o armazenamento de vetores Elasticsearch do zero.</a></p></li></ul><h2>Elasticsearch e Semantic Kernel: O que vem a seguir?</h2><ul><li><p>Mostramos como o armazenamento de vetores do Elasticsearch pode ser facilmente integrado ao Semantic Kernel durante a criação de aplicações GenAI em .NET. Fique atento para a integração com Python em breve.</p></li><li><p>À medida que o Semantic Kernel cria abstrações para recursos de pesquisa avançados, como <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">a pesquisa híbrida</a>, a conexão com o Elasticsearch permitirá que os desenvolvedores .NET os implementem facilmente ao usar o Semantic Kernel.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Evolução do cliente Elasticsearch .NET: do NEST ao Elastic.Clients.Elasticsearch]]></title>
    <description><![CDATA[Saiba mais sobre a evolução do cliente Elasticsearch .NET e a transição do NEST para o Elastic.Clients.Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>Introdução ao cliente .NET e ao NEST</h2><p>No mundo .NET, a integração com o Elasticsearch tem sido facilitada há muito tempo pela biblioteca <code>NEST</code> , que serve como uma interface robusta para os desenvolvedores interagirem com os poderosos recursos de pesquisa e análise do Elasticsearch. <code>NEST</code>, que surgiu da necessidade de um cliente .NET nativo para o Elasticsearch, rapidamente ganhou popularidade entre os desenvolvedores por seu rico conjunto de recursos e capacidades de integração perfeitas.</p><p>Durante quase <a href="https://github.com/elastic/elasticsearch-net/commit/724f932ba598915c8c3c35a19827fdfa4f782c1d">14 anos</a> e apenas 8 meses após <a href="https://github.com/elastic/elasticsearch/commit/ec72ca8b7a115f9b2eea3c76c518062b99a1d015">o primeiro commit do Elasticsearch,</a> o NEST tem acompanhado fielmente os lançamentos do Elasticsearch.</p><h2>Transição do NEST para o Elastic.Clients.Elasticsearch</h2><p>À medida que o Elasticsearch evoluiu, a manutenção da complexa base de código do <code>NEST</code> tornou-se cada vez mais difícil. Reconhecemos a necessidade de uma abordagem mais sustentável para o desenvolvimento de clientes e embarcamos em uma jornada para redesenhar o cliente .NET desde a sua base. Levamos quase um ano para lançar a primeira versão beta e mais um ano para chegar perto de oferecer suporte a todos os endpoints de servidor. Uma das decisões mais difíceis foi reduzir o escopo da biblioteca para priorizar a facilidade de manutenção.</p><p>Considerando o tamanho da superfície da API do Elasticsearch hoje, não é mais viável manter manualmente mais de 450 endpoints e quase 3000 tipos (requisições, respostas, consultas, agregações, etc.). Para garantir um alinhamento consistente, preciso e oportuno entre os clientes de linguagem e o Elasticsearch, os clientes da versão 8.x e muitos dos tipos associados agora são gerados automaticamente a partir de uma <a href="https://github.com/elastic/elasticsearch-specification">especificação compartilhada</a>. Essa é uma solução comum para manter o alinhamento entre cliente e servidor em SDKs e bibliotecas, como as do Azure, AWS e Google Cloud Platform.</p><p>A especificação do Elasticsearch foi criada há mais de 8 anos exportando os mapeamentos de tipo de <code>NEST</code> e, graças ao trabalho árduo da equipe de clientes, agora podemos usar a mesma especificação para criar um novo cliente .NET (e clientes para várias outras linguagens, como Java, Go, etc.).</p><p>Com o lançamento da versão 8.13, a descontinuação de <code>NEST</code> foi oficialmente anunciada. À medida que o Elasticsearch migra para <code>Elastic.Clients.Elasticsearch</code>, <code>NEST</code> será gradualmente descontinuado, chegando ao fim de sua vida útil no final do ano. Recomenda-se vivamente aos desenvolvedores que iniciem os esforços de migração o quanto antes para garantir uma transição tranquila e mitigar quaisquer potenciais interrupções. A adoção do <code>Elastic.Clients.Elasticsearch</code> não só garante a compatibilidade com os recursos mais recentes do servidor, como também protege os aplicativos contra funcionalidades obsoletas no futuro.</p><h2>Elastic.Clients.Elasticsearch: visão geral de recursos e alterações</h2><p>A mudança para o cliente v8 <code>Elastic.Clients.Elasticsearch</code> permite o acesso a todos os novos recursos do Elasticsearch 8 e também traz inúmeras modernizações para a própria biblioteca, mas também implica uma redução nos recursos de conveniência em comparação com seu antecessor. Algumas das novas funcionalidades principais incluem a linguagem de consulta <code>ES|QL</code>, capacidades modernas de aprendizagem automática (ML) e diagnósticos melhorados sob a forma de atividades compatíveis com OpenTelemetry. A partir da versão 8.13, <code>Elastic.Clients.Elasticsearch</code> suporta quase todos os recursos de servidor do Elasticsearch 8.</p><p>Uma importante mudança disruptiva, por exemplo, está relacionada às agregações. Em <code>NEST</code>, o uso da API fluente se parece com isto:</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
    .Children&lt;CommitActivity&gt;("name_of_child_agg", child =&gt; child
        .Aggregations(childAggs =&gt; childAggs
            .Average("average_per_child", avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor))
            .Max("max_per_child", max =&gt; max.Field(p =&gt; p.ConfidenceFactor))
            .Min("min_per_child", min =&gt; min.Field(p =&gt; p.ConfidenceFactor))
        )
    )
)
<p>enquanto o cliente v8 requer a seguinte sintaxe:</p>s =&gt; s
.Aggregations(aggs =&gt; aggs
	.Add("name_of_child_agg", agg =&gt; agg
		.Children(_ =&gt; {})
		.Aggregations(childAggs =&gt; childAggs
			.Add("average_per_child", agg =&gt; agg.Avg(avg =&gt; avg.Field(p =&gt; p.ConfidenceFactor)))
			.Add("max_per_child", agg =&gt; agg.Max(max =&gt; max.Field(p =&gt; p.ConfidenceFactor)))
			.Add("min_per_child", agg =&gt; agg.Min(min =&gt; min.Field(p =&gt; p.ConfidenceFactor)))
		)
	)
)
<h2>Migrando do NEST v7 para o cliente .NET v8</h2><p>Um guia de migração completo está disponível aqui: <a href="https://www.elastic.co/guide/en/elasticsearch/client/net-api/8.18/migration-guide.html">Guia de migração: do NEST v7 para o .NET Client v8</a>.</p><h2>Recursos adicionais</h2><ul><li><p><a href="https://github.com/elastic/elasticsearch-net">Elastic.Clients.Elasticsearch v8 Client no GitHub</a></p></li><li><p><a href="https://www.nuget.org/packages/Elastic.Clients.Elasticsearch">Elastic.Clients.Elasticsearch v8 Client no NuGet</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/net-client-evolution</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/net-client-evolution</guid>
    <category><![CDATA[.NET]]></category>
    <dc:creator><![CDATA[Florian Bernd]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltac0adcdaa85d703f/6a17f5816df731db170a1079/d09f7f5cb468d5e84f7f4636d92b3476e6604e11-1024x1024.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 16 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>