<?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[AI - 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[AI - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/pt/search-labs/blog/category/ai</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/blog/category/ai</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/category/ai.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 15:08:06 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Descreva, não desenhe: dashboards nativos de IA do Kibana via MCP e ES|QL]]></title>
    <description><![CDATA[Do prompt ao dashboard. Aprenda a construir dashboards do Kibana com linguagem natural, usando example-mcp-dashbuilder: uma aplicação MCP open source que escreve consultas ES|QL, cria gráficos interativos e exporta dashboards totalmente funcionais diretamente para Kibana.]]></description>
    <content:encoded><![CDATA[<p>O example-mcp-dashbuilder é um aplicativo MCP open source que transforma um prompt em inglês simples em um dashboard do Kibana ao vivo e interativo, tudo dentro da janela de bate-papo do seu editor. Descreva o dashboard desejado e a IA descobre sua estrutura de índice, escreve agregações ES|QL corretas para cada visualização e exibe uma pré-visualização embutida enquanto trabalha. Quando terminar, um comando exporta um dashboard do Kibana totalmente funcional: visualizações reais do Lens, layout exato da sua grade, cores personalizadas preservadas. Atualmente, há seis tipos de gráficos compatíveis, com o conjunto completo do Kibana Lens previsto no roadmap.</p><h2>O que é um construtor de dashboard do Kibana?</h2><p>E se você pudesse descrever o dashboard que deseja em inglês simples e vê-lo aparecer completo, com gráficos interativos, um layout de arrastar e soltar e exportação para o Kibana com um clique?</p><p>É exatamente isso que o <a href="https://github.com/elastic/example-mcp-dashbuilder.git"><strong>example-mcp-dashbuilder</strong></a> faz. É um aplicativo open source (Model Context Protocol (MCP)) que conecta assistentes de IA ao Elasticsearch, permitindo que você crie painéis completos do Kibana por meio de conversas. Sem precisar clicar nos menus. Sem escrever manualmente as configs de visualização. Basta descrever o que você precisa para que a IA explore seus dados, escreva as consultas Elasticsearch Query Language (ES|QL), crie os gráficos e forneça um dashboard interativo ao vivo, tudo dentro da janela de bate-papo do seu editor.</p><h2><strong>Do prompt ao dashboard em segundos</strong></h2><p>Veja como isso funciona na prática. Você digita algo como:</p><p>"Crie para mim um dashboard de tráfego da web a partir do logstash-* com total de solicitações, bytes transferidos ao longo do tempo, principais fontes geográficas e um detalhamento do código de resposta"</p><p>A IA então:</p><ol><li><p><strong>Descobre seus dados:</strong> lista índices e inspeciona mapeamentos de campos.</p></li><li><p><strong>Escreve consultas ES|QL:</strong> adaptadas ao seu esquema, usando as agregações corretas.</p></li><li><p><strong>Cria visualizações:</strong> gráficos de barras, gráficos de linhas, métricas com sparklines, mapas de calor, gráficos de pizza.</p></li><li><p><strong>Organiza tudo:</strong> seções retráteis, títulos significativos, layout adequado.</p></li><li><p><strong>Renderiza uma visualização interativa:</strong> diretamente no bate-papo, com dicas de ferramentas, um seletor de tempo e arrastar e soltar.</p></li></ol><p>Cada gráfico aparece em linha conforme é criado, então você pode ver o progresso em tempo real. Depois, <code>view_dashboard</code> mostra o dashboard completo com todos os painéis dispostos na grade de 48 colunas de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75af5d9042d141b5/6a17e99dbe608675a4004792/dcbf47c4f17bf1a184fb0167408ebeb861ef6c9d-1404x1568.png" alt="Dois gráficos exibidos na interface example‑mcp‑dashbuilder. O primeiro é um gráfico de barras verticais intitulado &quot;Principais fontes geográficas&quot;, mostrando a contagem de solicitações por código de país. O segundo é um gráfico circular intitulado &quot;Distribuição do código de resposta HTTP&quot;, mostrando segmentos para respostas 200, 404 e 503." /><p><em>Prévia de gráfico único em linha.</em></p><h2><strong>Desenvolvido por ES|QL</strong></h2><p>Toda recuperação de dados utiliza <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a>, a linguagem de consulta em pipeline do Elasticsearch. A IA não apenas passa por consultas brutas, ela também usa conhecimento integrado do ES|QL junto com informações sobre a estrutura dos seus dados para escrever consultas corretas e eficientes para cada tipo de visualização.</p><p>O servidor inclui uma referência abrangente de ES|QL como um recurso MCP. Antes de escrever qualquer consulta, a IA lê essa referência para entender os comandos, funções e padrões disponíveis. Em conjunto com um guia de **práticas recomendadas** de visualização de dados (que também serviu como recurso), a IA sabe não apenas <em>como</em> fazer consultas, mas <em>o que</em> torna uma visualização boa:</p><ul><li><p>Use <code>BUCKET(@timestamp, 1 day)</code> para séries temporais; sempre <code>SORT</code> pelo campo de tempo.</p></li><li><p>Limite os gráficos de pizza a seis fatias com <code>| SORT value DESC | LIMIT 6</code>.</p></li><li><p>Escolha gráficos de barras para comparações de categorias, gráficos de linhas para tendências, métricas para indicadores-chave de desempenho (KPIs).</p></li></ul><h2><strong>Exploração de dados orientada por IA com análise aberta</strong></h2><p>Construir um dashboard que você já imaginou na cabeça é outra história. Perguntar "O que há de interessante nesse índice?" e obter uma resposta útil é mais difícil; isso exige que a IA saiba como <em>explorar</em>, não apenas como desenhar.</p><p>O example-mcp-dashbuilder envia um recurso <code>analysis://guidelines</code> que define um fluxo de exploração estruturado: faça o perfil dos dados, execute agregações direcionadas, identifique padrões que valem a pena investigar, crie gráficos para as descobertas mais interessantes e proponha consultas detalhadas que o usuário possa querer em seguida. Frases gatilho, como "analisar meus logs" ou "encontrar padrões neste índice", fazem a IA ler o manual antes de fazer qualquer outra coisa, então um prompt aberto produz uma investigação coerente em vez de uma pilha aleatória de gráficos.</p><p>O resultado: você pode entregar um índice não familiar à IA e receber de volta um ponto de partida: um dashboard mais uma pequena lista de prompts "Aqui estão minhas impressões, quer que eu investigue mais a fundo algum desses?"</p><h2><strong>Exportação e importação do dashboard do Kibana: a viagem completa de ida e volta</strong></h2><p>A viagem de ida e volta de exportação/importação é onde o example-mcp-dashbuilder se torna realmente útil para as equipes que já trabalham com o Kibana. O example-mcp-dashbuilder é algo próprio, uma superfície de dashboard de conversação que fica dentro do seu editor, mas não prende o seu trabalho lá. Dashboards construídos aqui podem ser movidos para o Kibana quando você quiser, e dashboards existentes do Kibana podem seguir o caminho inverso para edição assistida por IA.</p><h3><strong>Exportar para Kibana</strong></h3><p>Quando você estiver satisfeito com seu dashboard, um comando irá exportá-lo:</p><p>"Exportar este dashboard para o Kibana"</p><p>Cada painel é traduzido para uma visualização real do Kibana Lens. A tradução preserva:</p><ul><li><p><strong>Consultas ES|QL:</strong> transferidas diretamente como fontes de dados ES|QL do Lens.</p></li><li><p><strong>Posições de grade:</strong> o mesmo sistema de 48 colunas que o Kibana usa, para que você tenha um layout idêntico.</p></li><li><p><strong>Cores personalizadas:</strong> paletas de séries, fundos métricos, rampas de cores de heatmap.</p></li></ul><p>O resultado é um dashboard do Kibana totalmente funcional. Não é uma captura de tela. Não é uma incorporação. Um dashboard do Kibana que você pode compartilhar e continuar editando.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1921c74c2833cabe/6a17e99f6864a4a712b687da/5e27777bc0a82cafb373943f65298bdb21d66176-1999x902.png" alt="Dois painéis são exibidos lado a lado. O dashboard esquerdo intitulado “Edição do tráfego na web — Logstash (Dashbuilder)” exibe métricas recentes de tráfego, junto com um painel de volume de tráfego, um gráfico de barras geográficas e um gráfico circular de código de resposta. O painel à direita apresenta um layout semelhante, porém com totais mais altos, e inclui um painel de volume de tráfego, um gráfico de barras geográficas e um gráfico de pizza de código de resposta." /><p><em>Dashboard do Kibana e dashboard no chat do Cursor lado a lado.</em></p><h3><strong>Importar do Kibana</strong></h3><p>A viagem de ida e volta também funciona na outra direção:</p><p>"Importar o dashboard do Kibana com o ID abc-123"</p><p>Isso busca um dashboard do Kibana existente, traduz suas visualizações do Lens para configurações de gráficos editáveis, preserva o layout e as seções da grade e carrega tudo no example-mcp-dashbuilder. A partir daí, você pode modificar com linguagem natural e reexportar.</p><p>Isso torna a IA uma colaboradora em seu fluxo de trabalho existente do Kibana, não uma substituta para ele.</p><h2><strong>Temas e cores personalizados</strong></h2><p>Quer um dashboard de marca? É só pedir:</p><p>"Crie um dashboard com tema rosa e cores personalizadas"</p><p>Todo tipo de visualização permite configuração de cor personalizada:</p><ul><li><p><strong>Gráficos:</strong> <code>palette</code> aceita uma matriz de cores hexadecimais para séries e fatias.</p></li><li><p><strong>Métricas:</strong> <code>color</code> define a cor de plano de fundo.</p></li><li><p><strong>Mapas de calor:</strong> <code>colorRamp</code> define o gradiente, dos valores baixos aos altos.</p></li></ul><p>A IA identifica os pedidos de tema naturalmente. Diga "tema do oceano", e ele vai escolher tons de azul e verde-azulado. Diga "Combine as cores da nossa marca" e forneça valores hexadecimais, e eles serão aplicados no Kibana na exportação.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2bc7cddbdef81354/6a17e9a1ec0f89ee155a665e/4aceba013ac9cbb4a541109efd6acddf8a6ec47d-1562x1568.png" alt="Um dashboard temático de comércio eletrônico com cores rosas personalizadas. O layout mostra os kpis de receita e pedidos na parte superior, uma seção de tendências resumida e dois gráficos baseados em categorias abaixo: um gráfico de barras para receita por categoria e um gráfico circular para pedidos por categoria." /><p><em>Um dashboard temático com cores personalizadas.</em></p><p><strong>Como funciona o example-mcp-dashbuilder: arquitetura MCP</strong></p><p>O example-mcp-dashbuilder foi desenvolvido com base no <a href="https://modelcontextprotocol.io/">MCP</a>, o padrão aberto para conectar assistentes de IA a ferramentas e dados externos. Aqui está a arquitetura em alto nível:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c0cd879646e9947/6a17e9a36864a4c408b687df/cbfeabe151ec1ee2b0655f4d17468c9bb358df7e-1024x559.png" alt="Um diagrama de arquitetura mostrando o MCP Host conectado ao MCP Server, que contém ferramentas, recursos e instruções. Abaixo dele, uma caixa do app MCP inclui Elastic Charts e o layout em grade do Kibana. Elasticsearch e Kibana aparecem na parte inferior, com setas conectando-os ao App MCP." /><p>O <strong>servidor MCP</strong> expõe 25 ferramentas que a IA pode chamar diretamente, desde a execução de consultas ES|QL até a exportação de painéis, além de algumas ferramentas internas "exclusivas do app" que a pré-visualização embutida usa para buscar dados, persistir alterações de layout e detectar campos de tempo. Ele oferece três recursos: um guia de **práticas recomendadas** de dataviz, uma referência ES|QL e um manual de análise aprofundada que entra em ação para prompts abertos ("analisar meus logs", "o que há de interessante neste índice"). E executa tanto em stdio quanto em HTTP; o transporte HTTP permite respostas em fluxo contínuo e gerenciamento de sessão, permitindo que vários clientes se conectem a um mesmo servidor.</p><p>O <strong>MCP App</strong> é uma pré-visualização interativa. Ele foi desenvolvido com React, <a href="https://elastic.github.io/elastic-charts">Elastic Charts</a> e <a href="https://eui.elastic.co/">Elastic UI</a>, agrupados em um único arquivo HTML independente. Quando a IA chama <code>view_dashboard</code> ou cria um gráfico, o host renderiza este HTML em um iframe isolado. O aplicativo se comunica com o servidor inteiramente através do <a href="https://modelcontextprotocol.io/extensions/apps/overview">protocolo MCP Apps</a>, usando <code>callServerTool()</code> sobre postMessage para buscar dados, salvar layouts e detectar campos de tempo. Não há servidor localhost, nenhuma porta para configurar, nenhuma dependência de rede externa.</p><p>Isso significa que funciona com qualquer cliente compatível com MCP: Cursor, Claude Desktop, Claude.ai, VS Code com Copilot e muito mais.</p><h2><strong>Quais tipos de gráficos o example-mcp-dashbuilder permite?</strong></h2><p>No momento desta publicação, são permitidos seis tipos de gráficos que cobrem os cenários de dashboard mais comuns:</p><p>Tipo</p><p>Melhor para</p><p>Exemplo</p><p>Barra</p><p>Comparando categorias</p><p>Solicitações por fonte geográfica</p><p>Linha</p><p>Tendências ao longo do tempo</p><p>Bytes transferidos por hora</p><p>Área</p><p>Volume ao longo do tempo</p><p>Volume de solicitações ao longo do tempo</p><p>Pizza</p><p>Parte do todo (máximo seis fatias)</p><p>Distribuição de código de resposta</p><p>Métrica</p><p>KPI único com sparkline</p><p>Total de solicitações com tendência horária</p><p>Heatmap</p><p>Padrões em duas dimensões</p><p>Solicitações por dia da semana e hora</p><p>Dashboards permitem seções recolhíveis para organização, um seletor de tempo com detecção automática de campos de tempo e a capacidade de salvar e alternar entre múltiplos dashboards; sessões paralelas de chat permanecem isoladas umas das outras por meio de um <code>dashboardId</code> que passa por cada chamada de ferramenta.</p><h2><strong>Como instalar e executar o example-mcp-dashbuilder</strong></h2><p>O example-mcp-dashbuilder é open source e está pronto para uso. Você vai precisar de Node.js 22+, uma instância Elasticsearch (local ou Elastic Cloud) e um cliente compatível com MCP.</p><p><strong>Claude Desktop:</strong> baixe a versão mais recente <code>.mcpb</code> do <a href="https://github.com/elastic/example-mcp-dashbuilder/releases">GitHub Releases</a>, e clique duas vezes nela. O Claude Desktop solicitará suas credenciais do Elasticsearch.</p><p><strong>Cursor / Claude Code / VS Code Copilot:</strong> aponte sua configuração MCP para o tarball liberado; sem clone, sem <code>npm install</code>:</p>{
  "mcpServers": {
    "example-mcp-dashbuilder": {
      "type": "stdio",
      "command": "npx",
      "args": ["https://github.com/elastic/example-mcp-dashbuilder/releases/latest/download/example-mcp-dashbuilder.tgz"]
    }
  }
}<p>Configure <code>ES_NODE, ES_API_KEY</code> (ou <code>ES_USERNAME / ES_PASSWORD</code>) e <code>KIBANA_URL</code> como variáveis de ambiente. Se você preferir trabalhar a partir da fonte, clone o repositório e execute <code>npm run setup</code> para um assistente interativo que lida com o Elasticsearch local e o Elastic Cloud (Cloud ID + chave de API).</p><p>E comece a construir:</p><p>"Explore o índice de logs e construa o dashboard mais perspicaz que puder"</p><p>A partir daí, a IA assume o controle. 😉</p><h2><strong>Roadmap: o que está por vir para o example-mcp-dashbuilder</strong></h2><p>Este é um lançamento antecipado, e estamos em desenvolvimento ativo. Algumas áreas em que estamos focados:</p><ul><li><p><strong>Mais tipos de gráficos:</strong> medidor, donut, treemap, tabela de dados e nuvem de tags para combinar com todas as capacidades da Lens.</p></li><li><p><strong>Envie dashboards para o Git: </strong>escreva configurações de dashboards em um repositório para fluxo de trabalho de controle de versões e revisão de código.</p></li><li><p><strong>Melhor UX de erro: </strong>feedback mais detalhado quando o ES|QL falha, com sugestões comuns de correções.</p></li><li><p><strong>Fluxos de análise mais ricos: </strong>estenda o manual de análise profunda para cobrir mais formas de dados (logs, métricas, rastreamentos).</p></li></ul><p>Adoraríamos saber o que você cria com ele. Experimente, registre problemas e conte para a gente quais visualizações e fluxos de trabalho seriam mais úteis para sua equipe.</p><p><a href="https://github.com/elastic/example-mcp-dashbuilder">GitHub: elastic/example-mcp-dashbuilder</a></p><h3>Agradecimentos</h3><p>Agradecemos a <a href="mailto:walter.rafelsberger@elastic.co">Walter Rafelsberger</a> e <a href="mailto:tim.schnell@elastic.co">Tim Schnell</a> por suas contribuições para a implementação.</p><h3>Perguntas frequentes</h3><p><strong>O que é o example-mcp-dashbuilder?</strong> o example-mcp-dashbuilder é um aplicativo MCP (Model Context Protocol) open source que conecta assistentes de IA ao Elasticsearch. Ele permite que você descreva um dashboard do Kibana e automaticamente gera consultas ES|QL, cria visualizações e entrega um dashboard interativo ao vivo dentro da janela de chat do seu editor.</p><p><strong>Qual linguagem de consulta o example-mcp-dashbuilder usa para recuperar dados?</strong> Toda recuperação de dados utiliza ES|QL, a linguagem de consulta com barras verticais do Elasticsearch. O servidor MCP inclui uma referência ES|QL integrada que a IA lê antes de escrever qualquer consulta, garantindo a sintaxe correta e agregações eficientes para cada tipo de visualização.</p><p><strong>Posso exportar dashboards construídos com example-mcp-dashbuilder para Kibana?</strong> Sim. Executar "Exportar este dashboard para Kibana" traduz todos os painéis em uma visualização real do Kibana Lens, preservando as consultas ES|QL, o layout de grade de 48 colunas, cores personalizadas e paletas de séries. O resultado é um dashboard do Kibana totalmente funcional, não uma captura de tela ou incorporação.</p><p><strong>Posso importar um dashboard do Kibana existente para o example-mcp-dashbuilder para edição assistida por IA?</strong> Sim. Fornecer um ID de dashboard do Kibana busca o dashboard existente, traduz suas visualizações do Lens em configurações de gráfico editáveis e as carrega no example-mcp-dashbuilder. Você pode então modificar o dashboard usando linguagem natural e reexportar para o Kibana.</p><p><strong>Quais clientes MCP são compatíveis com o example-mcp-dashbuilder?</strong> O example-mcp-dashbuilder funciona com qualquer cliente compatível com MCP, incluindo Cursor, Claude Desktop, Claude.ai e VS Code com Copilot. Ele permite tanto transporte stdio quanto HTTP, sem necessidade de configuração de servidor localhost ou de porta.</p><p><strong>Quais tipos de gráficos o example-mcp-dashbuilder permite?</strong> A versão atual permite seis tipos de gráficos: barra, linha, área, pizza, métrica (com sparkline) e heatmap. As adições planejadas incluem indicador, rosca, mapa de árvore, tabela de dados e nuvem de tags para combinar com todas as capacidades do Kibana Lens.</p><p><strong>O que eu preciso para executar o example-mcp-dashbuilder?</strong> Você precisa do Node.js versão 22 ou superior, uma instância do Elasticsearch (local ou Elastic Cloud) e um cliente compatível com MCP. Defina as variáveis de ambiente ES_NODE, ES_API_KEY (ou ES_USERNAME/ES_PASSWORD) e KIBANA_URL. Para o Claude Desktop, baixe o arquivo .mcpb do GitHub Releases e clique duas vezes para instalar.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/kibana-dashboard-builder-mcp-esql</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/kibana-dashboard-builder-mcp-esql</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Stratoula Kalafateli]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a69a35d6d51ff47/6a17e9a5b1e11339cd79f2b3/0d38385fd64c1445b2e955ba20532570f7f38679-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 22 May 2026 00:00:00 GMT</pubDate>
  </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>
    <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 </em><em><strong>com vários tipos diferentes</strong></em><em> 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><p>Convenções culturais de nomeação.</p></li><li><p>Referências baseadas em títulos.</p></li><li><p>Relações comerciais e mudanças históricas de nome.</p></li><li><p>Menções multilíngues e em diferentes sistemas de escrita.</p></li><li><p>Desafios complexos que misturam vários dos itens acima.</p></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><p><strong>50 entidades</strong>, abrangendo pessoas, organizações e instituições.</p></li><li><p><strong>Cerca de 60 artigos</strong>, com estrutura e complexidade linguística variadas.</p></li><li><p><strong>51 categorias distintas de desafios</strong>, agrupadas de forma ampla em:</p><ul><li><p>Convenções culturais de nomeação.</p></li><li><p>Títulos e o contexto profissional.</p></li><li><p>Relacionamentos empresariais e organizacionais.</p></li><li><p>Desafios multilíngues e de transliteração.</p></li><li><p>Cenários combinados e casos limite.</p></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 "o presidente russo" 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: "A nova atualização do Swift chegou!" O desafio é que "Swift" 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><p>Um título como “o presidente” não tem significado sem contexto geopolítico e temporal.</p></li><li><p>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.</p></li><li><p>O nome de uma pessoa pode aparecer em diferentes ordens, sistemas de escrita ou transliterações, dependendo da língua e da cultura.</p></li><li><p>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.</p></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><p>O Elasticsearch reduz o conjunto de candidatos de forma eficiente e transparente.</p></li><li><p>O LLM é usado apenas quando o julgamento é necessário e é obrigado a se explicar.</p></li><li><p>Recuperação e raciocínio continuam sendo etapas distintas.</p></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><p><strong>Não</strong> adicionamos lógica especial para nomes japoneses.</p></li><li><p>Não <strong>adicionamos</strong> regras personalizadas para patronímicos árabes.</p></li><li><p><strong>Não</strong> adicionamos mapeamentos fixos para nomes históricos de empresas.</p></li></ul><p>Em vez disso, o sistema se baseou nos mesmos elementos centrais apresentados anteriormente na série:</p><ul><li><p>Entidades enriquecidas por contexto indexadas para busca semântica.</p></li><li><p>Recuperação híbrida (exata, alias e semântica) no Elasticsearch.</p></li><li><p>Um pequeno e bem definido conjunto de correspondências candidatas.</p></li><li><p>Julgamento de LLM restrito por chamada de função e esquemas mínimos.</p></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><p><strong>Precisão:</strong> ~91%</p></li><li><p><strong>Recall:</strong> ~86%</p></li><li><p><strong>Pontuação F1:</strong> ~89%</p></li><li><p><strong>Taxa de aceitação em LLM:</strong> ~72%</p></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><p>Correspondência de entidades entre sistemas de escrita (cirílico, coreano e chinês).</p></li><li><p>Cenários hebraicos (patronímicos, títulos profissionais, títulos religiosos, transliteração).</p></li><li><p>Hierarquias de negócios (aeroespacial, manufatura diversificada, corporações multidivisionais).</p></li><li><p>Títulos profissionais (acadêmicos, militares, políticos, religiosos).</p></li><li><p>Cenários combinados em japonês envolvendo múltiplos sistemas de escrita.</p></li></ul><p><strong>Forte desempenho (pontuação F1 de 80–99%)</strong> incluiu:</p><ul><li><p>Figuras políticas internacionais (98%).</p></li><li><p>Alterações históricas de nome (90%).</p></li><li><p>Hierarquias empresariais complexas (89%).</p></li><li><p>Nomes de empresas japonesas (93%).</p></li><li><p>Transliteração entre escrituras (86%).</p></li><li><p>Patrônimos árabes (86%).</p></li></ul><p><strong>Áreas mais desafiadoras</strong> incluíram:</p><ul><li><p>Transliteração avançada (chinês, coreano): 0% de pontuação F1.</p></li><li><p>Certos cenários japoneses (honoríficos, ordem dos nomes, variação do sistema de escrita): ~67% F1.</p></li><li><p>Alguns cenários árabes (nomes de empresas, referências institucionais): ~40% F1.</p></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><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></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.
</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/entity-resolution-elasticsearch-llm-challenges</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Busca híbrida]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc58be329ffebcd60/6a17043e47d49c0bc62d88ab/70fb0ff949f6db9ac9b8a28ecb4329ab915ebf46-720x420.png" length="0" type="image/png"/>
    <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
  </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>
    <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: "A atualização Swift chegou!" Imagine que esta manchete vem acompanhada de um pouco mais de contexto:</p><ol><li><p>A nova atualização do Swift chegou! Os desenvolvedores estão ansiosos para experimentar os novos recursos.</p></li><li><p>A nova atualização do Swift chegou! O novo álbum será lançado no próximo mês.</p></li></ol><p>Com esse contexto adicional, devemos conseguir resolver o nome "Swift" 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 "Swift". 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 "codificado por vibração" 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 "o presidente", 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 "gabarito" 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 "trapacear" 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 "autor russo" para "Liev Tolstói". O LLM incorretamente colocou "autor russo" como um alias para "Leo Tolstoy", 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 "desafio supremo", 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><p><strong>Entidades da lista de observação:</strong> 66 entidades de diversos tipos (pessoas, organizações, locais).</p></li><li><p><strong>Artigos de teste:</strong> 69 artigos que abrangem cenários reais de resolução de entidades.</p></li><li><p><strong>Correspondências esperadas:</strong> 206 correspondências de entidades esperadas em todos os artigos.</p></li><li><p><strong>Tipos de desafio: </strong>15 tipos diferentes de desafio que testam vários aspectos da resolução de entidades.</p></li></ul><p>Os tipos de desafios incluídos no conjunto de dados são:</p><ul><li><p><strong>Apelidos:</strong> "Bob Smith" → "Robert Smith" (sete artigos).</p></li><li><p><strong>Títulos e honoríficos:</strong> "Dr. Sarah Williams" → "Sarah Williams" (cinco artigos).</p></li><li><p><strong>Contexto semântico:</strong> "autor russo" → "Liev Tolstói" (oito artigos).</p></li><li><p><strong>Nomes multilíngues:</strong> manuseio de nomes em diferentes scripts (seis artigos).</p></li><li><p><strong>Entidades empresariais:</strong> variações de nome corporativo (sete artigos).</p></li><li><p><strong>Referências executivas: </strong>"CEO da Microsoft" → "Satya Nadella" (cinco artigos).</p></li><li><p><strong>Líderes políticos:</strong> referências baseadas em títulos (cinco artigos).</p></li><li><p><strong>Iniciais:</strong> "J. Smith" → "John Smith" (três artigos).</p></li><li><p><strong>Variações na ordem dos nomes:</strong> diferentes convenções de ordenação de nomes (três artigos).</p></li><li><p><strong>Nomes truncados:</strong> correspondências parciais de nomes (três artigos).</p></li><li><p><strong>Divisão de nomes:</strong> nomes divididos no texto (três artigos).</p></li><li><p><strong>Falta de espaços/hífens:</strong> variações de formatação (dois artigos).</p></li><li><p><strong>Transliteração:</strong> correspondência de nomes entre escrituras (dois artigos).</p></li><li><p><strong>Desafios combinados:</strong> Vários desafios em um único artigo (seis artigos).</p></li><li><p><strong>Negócios complexos:</strong> relações comerciais hierárquicas (cinco artigos).</p></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><p>Métrica</p><p>Valor</p><p>Precisão</p><p>83,8%</p><p>Recall</p><p>62,6%</p><p>Pontuação F1</p><p>71,7%</p><p>Total de correspondências encontradas</p><p>344</p><p>Taxa de aceitação do LLM</p><p>44,8%</p><p>Taxa de erro</p><p>30,2%</p><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><p>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.</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-entity-resolution-llm-semantic-search</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Busca híbrida]]></category>
    <dc:creator><![CDATA[Jessica Moszkowicz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltefc59243d9990405/6a17056ab339d5778f769ebf/473ca4357c7d60f690edbd2a844acda169aca9c3-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate>
  </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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte8f5bd0e4986b04c/6a170e6acdacbf612e7d2a9e/9108ec303339dd091faa3c363c7cf5c228155f49-3840x2160.png" alt="" /><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"><u>Streams</u></a>!</p><h2>Descrição do conjunto de dados</h2><p>Escolhemos uma coleção de logs do <a href="https://github.com/logpai/loghub"><strong>Loghub</strong></a>para fins de PoC. Para nossa investigação, selecionamos amostras representativas das seguintes áreas-chave:</p><ul><li><p>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.</p></li><li><p>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.</p></li><li><p>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.</p></li><li><p>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.</p></li><li><p>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.</p></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>[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<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 "Cachinhos Dourados" 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><p>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.</p></li><li><p>Muito heterogêneo (confusão): por outro lado, se a amostra contiver uma variação significativa de formatação, ou pior, "registros de lixo" 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.</p></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 "confusa" 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><p>Variações de nomenclatura de serviço: o modelo propõe diferentes nomes para a mesma entidade (por exemplo, rotulando a fonte como "Spark", "Apache Spark" e "Spark Log Analytics" em diferentes execuções).</p></li><li><p>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>.</p></li><li><p>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.</p></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 "impressão digital" simplificada que nos permite agrupar logs relacionados.</p><p>A lógica de mapeamento é simples para garantir velocidade e consistência:</p><ol><li><p>Abstração de dígitos: qualquer sequência de dígitos (0-9) é substituída por um único "0".</p></li><li><p>Abstração de texto: qualquer sequência de caracteres alfabéticos com espaço em branco é substituída por um único "a".</p></li><li><p>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.</p></li><li><p>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.</p></li></ol><p>Apresentamos a abordagem de mapeamento de log. Os padrões básicos de mapeamento incluem os seguintes:</p><ul><li><p>Dígitos de 0 a 9 de qualquer comprimento -&gt; até "0".</p></li><li><p>Texto (caracteres alfabéticos com espaços) de qualquer comprimento -&gt; para "a".</p></li><li><p>Espaços em branco, abas e novas linhas -&gt; para um único espaço.</p></li></ul><p>Vamos ver um exemplo de como esse mapeamento nos permite transformar os logs.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf91eebab0ad79ccd/6a170e6c67045ba94f45c29c/78fa2887486eb9417804354ee3bf2a4fdb0f6383-846x252.png" alt="" /><p>Como resultado, obtemos as seguintes máscaras de log:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt438d74dcb921578b/6a170e6d1949f74aa0e7aae3/ec439a3d3a25002498b97defcff733ea5ebc6b55-826x94.png" alt="" /><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><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 '0', texto para 'a', etc.) e salvamos o resultado em um campo "padrão".</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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfa3960cf94ccf331/6a170e6fdc55decfa3e00e7c/b119498f124376c41d242a099bf9081fd6536be8-1600x394.png" alt="Resultados da consulta de análise de logs no LogHub." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dbcde2a22e06367/6a170e71961e693a18c4cfb6/4dcfc0a5b7fa753497cc5def5ea3cd54449c0481-1600x719.png" alt="" /><p>Como demonstrado na visualização, essa abordagem "livre de LLM" 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><p>Versatilidade como primitiva </p></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><p>Flexibilidade</p></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><p>Estabilidade determinística</p></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><p>Desempenho e memória</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26635762891b3a41/6a170e73509168eea4e1bb91/b3f46ea471760b406a32fc7d4bc74cc03faaced2-3840x1660.png" alt="" /><p>1. Clustering hierárquico em dois estágios</p><ul><li><p>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.</p></li><li><p>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.</p></li><li><p>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.</p></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><p>Selecionamos logs representativos de <em>cada</em> subclasse válida dentro da metaclasse mais ampla.</p></li><li><p>Para gerenciar um caso extremo de subclasses muito numerosas, aplicamos subamostragem aleatória para ajustar ao tamanho da janela alvo.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b896b41b3b70e7e/6a170e757d8d67601a70e7d9/49b2b6a1401dd1f33951da68e5a3fac37d0b5aaa-1600x1506.png" alt="Cerca de 94% de precisão de análise sintática e 91% de precisão de particionamento no conjunto de dados do Loghub." /><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><p><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.</p></li><li><p><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.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/log-parsing-partitioning-automation-experiments-streams</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/log-parsing-partitioning-automation-experiments-streams</guid>
    <category><![CDATA[Pesquisa de aprendizado de máquina]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Nastia Havriushenko]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1df5a7cae463d59/6a170e76a6c2b907d7e797ab/965c58f19742361160593c38fcaa8b2f4b0d6cc5-3838x2159.png" length="0" type="image/png"/>
    <pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate>
  </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>
    <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><p>Um editor de texto rodando no seu computador local</p><ul><li><p><a href="https://code.visualstudio.com/download">Visual Studio Code</a> é o que usaremos para as instruções de exemplo neste post do blog</p></li></ul></li><li><p><a href="https://www.python.org/downloads/">Python 3.10 ou superior</a> rodando no seu computador local</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaedaa94068c07a17/6a17060f961e697558c4ce5f/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" alt="" /><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 "game-docs".</p>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt430c357b479d93af/6a170611a6c2b98191e79624/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" alt="" /><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>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"
}<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64d49e13754d5b25/6a17061214b270524be3c55d/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d374bf2ba9135c/6a1706147d8d67468570e63e/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" alt="" /><p>Agora, é preciso perguntar ao Elastic AI Agent padrão: "Quais dados eu tenho?"</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0f879cf28772718/6a1706161949f7f25ee7a92d/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7802a6b94e81440c/6a170618ab7f085287db9db4/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" alt="" /><p>Selecione <strong>Ver todas as ferramentas </strong>nas opções do menu.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f52ffe114fb6ea7/6a17061a4a531b801b36a884/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" alt="" /><p>Clique <strong>+ Nova Ferramenta.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8090769f6c4d1899/6a17061c286714294093e219/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" alt="" /><p>No formulário <strong>Criar Ferramenta</strong>, selecione <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql"><strong>ES|QL.</strong></a>Selecione a ferramenta <strong>Tipo</strong> e insira os valores a seguir.</p><p>Para o <strong>ID da Ferramenta</strong>:</p>example.get_game_docs<p>Para <strong>Descrição</strong>:</p>Get RPS+ doc from Elasticsearch game-docs index.<p>Para <strong>Configuração, </strong>insira a seguinte consulta na área de texto <strong>Mecanismo de consulta ES|QL: </strong></p>FROM game-docs | WHERE filename == "RPS+.md"<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77034c305198217a/6a17061e66c4f9e54ef8bf5e/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa8a83fc2f3758a9/6a1706201949f71a10e7a931/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" alt="" /><p>Clique<strong> + Novo agente.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3778403c5101a000/6a17062160084be12f3c449e/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" alt="" /><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>rps_plus_agent<p>Na área de texto de <strong>Instruções personalizadas, </strong>insira as seguintes instruções:</p>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.<p>Para o <strong>Nome de exibição, </strong>insira o texto abaixo:</p>RPS+ Agent<p>Para a <strong>Descrição de exibição, </strong>insira o texto abaixo:</p>An agent that plays the game RPS+<p>Dê ao agente a ferramenta personalizada que criamos anteriormente, clicando na guia <strong>Ferramentas</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b0fe00abdde07c/6a17062314b2704bc4e3c563/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" alt="" /><p>Selecione somente a ferramenta <em>example.get_game_docs</em> que criamos anteriormente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2210212e07e06104/6a170625a929cf3277ae08d1/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" alt="" /><p>Clique em <strong>Salvar</strong> para criar o novo agente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e3afc1918e26f14/6a170627ab7f084746db9db8/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb4b69dc5971d3a0/6a1706286f7f046840914743/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" alt="" /><p>Basta digitar “iniciar jogo” e o jogo começará. Funciona!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b621d602223dff/6a17062ab339d568a1769ef8/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73901ec745a97fbf/6a17062c964cea23c808bab3/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de752025d62993f/6a17062d0c4857f16501a997/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" alt="" /><p>No terminal recém-aberto, execute o seguinte comando para clonar o repositório Elasticsearch Labs:</p>git clone https://github.com/elastic/elasticsearch-labs<p>Execute o seguinte <em>cd </em>comando para alterar o diretório para o diretório elasticsearch-labs:</p>cd elasticsearch-labs<p>Em seguida, execute o seguinte comando para abrir o repositório no Visual Studio Code:</p>code .<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65ef8036a70bcaf1/6a17062f1949f7af36e7a935/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" alt="" /><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>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) -&gt; 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())<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 <em><code>httpx</code></em><code> 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 "iniciar jogo" ao agente. Uma coisa interessante a notar aqui é que incluímos um valor <code>random_game_object</code> como parte do pedido <code>"start game"</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 "iniciar jogo", 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta17961cbcb42985c/6a170631b0367dc5a072bc55/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt140885d7ebfcb969/6a1706327d8d67b17670e646/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc153c2caa27e949b/6a170634a292997793d00f6d/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" alt="" /><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>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltada5de819f31d8ff/6a170635b339d55ae9769efc/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" alt="" /><p>Clique no <strong>botão Copiar chave API </strong>para copiar a chave API.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18f85790df00706/6a170637cf4f257145b2d0bd/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92ab4b37cdcca85e/6a1706386f7f0472ed914747/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" alt="" /><h2>Execute o app de exemplo</h2><p>Abra um novo terminal no Visual Studio Code.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8702d826849755d0/6a17063a60084b45ca3c44a2/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" alt="" /><p>Comece executando o seguinte comando <em>cd</em> no terminal:</p>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents<p>Execute o seguinte comando para criar um ambiente virtual Python.</p>python -m venv .venv<p>Dependendo do sistema operacional do seu computador local, execute o seguinte comando para ativar o ambiente virtual.</p><ul><li><p>MacOS/Linux</p></li></ul>source .venv/bin/activate<ul><li><p>Windows</p></li></ul>.venv\Scripts\activate<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>pip install -r requirements.txt<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>python elastic_agent_builder_a2a_rps+.py<p>Você deve ser desafiado com uma partida de RPS+. Parabéns e boa sorte!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb3715672995fcfa/6a17063c6234e07b76db195f/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" alt="" /><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>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p><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> "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."</p></li><li><p><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.”</p></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><p>APIKey do Elasticsearch</p></li><li><p>APIKey do OpenAPI</p></li><li><p>Node 18 ou mais recente</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90db8f03f372608c/6a170986dc55de6e16e00d93/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" alt="" /><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><p><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.</p></li><li><p><strong>prepareInvestSearch: </strong>extrai valores de filtro da consulta e constrói um modelo pré-definido enfatizando parâmetros financeiros e relacionados ao financiamento.</p></li><li><p><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.</p></li><li><p><strong>executeSearch: </strong>envia a consulta construída para o Elasticsearch usando um modelo de busca e recupera os documentos correspondentes de inicialização.</p></li><li><p><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.</p></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>const VCState = Annotation.Root({
  input: Annotation&lt;string&gt;(), // User's natural language query
  searchStrategy: Annotation&lt;string&gt;(), // Search strategy chosen by LLM
  searchParams: Annotation&lt;any&gt;(), // Prepared search parameters
  results: Annotation&lt;any[]&gt;(), // Search results
  final: Annotation&lt;string&gt;(), // Final formatted response
});<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>npm init -y<p>Agora podemos instalar as dependências necessárias para este projeto:</p>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod &amp;&amp; npm install --save-dev @types/node tsx typescript<ul><li><p><strong><code>@elastic/elasticsearch</code></strong>: Nos ajuda a lidar com requisições do Elasticsearch, como ingestão e recuperação de dados.</p></li><li><p><strong><code>@langchain/langgraph</code></strong>: dependência de JS para fornecer todas as ferramentas LangGraph.</p></li><li><p><strong><code>@langchain/openai</code></strong>Cliente OpenAI LLM para LangChain.</p></li><li><p>@langchain/núcleo: fornece os blocos de construção fundamentais para apps LangChain, incluindo modelos de prompt.</p></li><li><p><strong><code>dotenv</code></strong>: Dependência necessária para usar variáveis de ambiente em JavaScript.</p></li><li><p><strong><code>zod</code></strong>: Dependência para digitar dados.</p></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><p><code>elasticsearchSetup</code><a href="http://ingest.ts/"><code>.ts</code></a>: Criará os mapeamentos de índice, carregará o conjunto de dados de um arquivo JSON e fará a ingestão dos dados no Elasticsearch.</p></li><li><p><a href="http://main.ts/"><code>main.ts</code></a>: incluirá o aplicativo LangGraph.</p></li><li><p><code>.env</code>: arquivo para armazenar as variáveis de ambiente</p></li></ul><p>No arquivo <code>.env</code>, vamos adicionar as seguintes variáveis de ambiente:</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<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>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" });<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>async function saveGraphImage(app: any): Promise&lt;void&gt; {
  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);
  }
}<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>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 {};
  }
}<p>Com a consulta de agregação acima, temos os seguintes resultados:</p>{
  "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
  }
}<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>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
            }
          }
        }`,
      },
    });<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>// 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",
    };
  }
}<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>// 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);
}<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>// 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: {},
    };
  }
}<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>// 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 {};
  }
}<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>// 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) =&gt; hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}<h3>nó visualizeResults</h3><p>Por fim, este nó exibe os resultados do Elasticsearch.</p>// 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) =&gt; {
    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,
  };
}<p>Programaticamente, o gráfico completo tem a seguinte aparência:</p>  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) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow<p>Como você pode ver, temos uma aresta condicional onde o app decide qual "caminho" 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>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) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; 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);
}<p>A variável de consulta simula a entrada do usuário inserida em uma barra de busca hipotética:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba7189d5f4e63403/6a1709880e2e49cc3041a076/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" alt="" /><p>A partir da frase em linguagem natural "Encontre startups com financiamento Série A ou Série B entre US$ 8M–US$ 25M e receita mensal acima de US$ 500K", todos os filtros serão extraídos.</p><p>Finalmente, invoque o método principal:</p>main().catch(console.error);<h3>Resultados</h3>🔍 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.<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 "Encontre startups de fintech e saúde em São Francisco, Nova York ou Boston":</p>...

🔍 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.<h2>Aprendizados</h2><p>Durante o processo de escrita, aprendi:</p><ul><li><p>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.</p></li><li><p>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</p></li><li><p>Arestas condicionais são um mecanismo poderoso para construir aplicações com múltiplas variantes e caminhos ramificados.</p></li><li><p>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.</p></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>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt013eba5d152f11f3/6a1709892b835f6784f4b1a6/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png" length="0" type="image/png"/>
    <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p><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.</p></li><li><p>Recomenda-se máquina com 32 GB de RAM (mínimo de 16 GB para GPT-OSS 20B).</p></li><li><p><a href="https://lmstudio.ai/">LM Studio</a> instalado</p></li><li><p><a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a> instalado</p></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><p><strong>capacidades de utilização da ferramenta</strong></p></li><li><p><strong>Inferência eficiente</strong></p></li><li><p><strong>Compatível com o SDK OpenAI</strong></p></li><li><p><strong>Compatível com fluxos de trabalho agentes</strong></p></li></ul><p>Comparação de referência:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt58fab956edb40412/6a170cfcb0367da43a72bd80/29160e3345352088e8213297630882f252b00c47-1600x680.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80db5bb0a797f51b/6a170cfd0e2e492f2c41a16f/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" alt="" /><h2>Construindo um agente de IA para RH: Etapas</h2><p>Dividiremos a implementação em 5 etapas:</p><ol><li><p>Configure o LM Studio com um modelo local.</p></li><li><p>Implante o Elastic local com o Docker.</p></li><li><p>Crie o conector OpenAI no Elastic</p></li><li><p>Carregar dados de funcionários no Elasticsearch</p></li><li><p>Crie e teste seu agente de IA.</p></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 o LM Studio a partir do site oficial. Após a instalação, abra o aplicativo.</p><h3>Na interface do LM Studio:</h3><ol><li><p>Acesse a aba de pesquisa e procure por “GPT-OSS”.</p></li><li><p>Selecione o <code>openai/gpt-oss-20b</code> da OpenAI</p></li><li><p>Clique em baixar</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dc341a6625e34b7/6a170cff839dfa2eb4dcff44/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" alt="" /><h4>Após o download do modelo:</h4><ol><li><p>Acesse a aba do servidor local.</p></li><li><p>Selecione o openai/gpt-oss-20b</p></li><li><p>Use a porta padrão 1234</p></li><li><p>No painel direito, acesse <strong>Carregar </strong>e defina o Comprimento do Contexto para <strong>40K</strong> ou mais.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3704ca1b28465cc4/6a170d00d7c022ed8fde64ef/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" alt="" /><p>5. Clique em Iniciar servidor</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b9170a4945ff857/6a170d0266c4f9ffadf8c0a6/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" alt="" /><p>Você deverá ver isso se o servidor estiver em execução.</p>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] -&gt;	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/embeddings
Server started.<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>curl -fsSL https://elastic.co/start-local | sh<p>Este script irá:</p><ul><li><p>Baixe e configure o Elasticsearch e o Kibana.</p></li><li><p>Inicie ambos os serviços usando o Docker Compose.</p></li><li><p>Ative automaticamente uma licença de avaliação Platinum de 30 dias.</p></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>🎉 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<h3>Acesse o Kibana</h3><p>Abra seu navegador e acesse:</p>http://localhost:5601<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a934bd99fa6a0ce/6a170d046234e019c3db1a5a/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" alt="" /><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><p>Em Kibana</p></li><li><p>Acesse <strong>Configurações do projeto</strong> &gt; <strong>Gerenciamento</strong></p></li><li><p>Em <strong>Alertas e insights</strong>, selecione <strong>Conectores.</strong></p></li><li><p>Clique em Criar conector</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt762023c39781eb78/6a170d06a29299a59ed01087/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" alt="" /><p>Preencha os campos com estes valores:</p><ul><li><p><strong>Nome do conector: </strong>LM Studio - GPT-OSS 20B</p></li><li><p><strong>Selecione um provedor OpenAI: </strong>Outro (Serviço compatível com OpenAI)</p></li><li><p><strong>URL: </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></p></li><li><p><strong>Modelo padrão: </strong>openai/gpt-oss-20b</p></li><li><p><strong>Chave da API:</strong> testkey-123 (qualquer texto funciona, pois o LM Studio Server não requer autenticação).</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt980e595f80e2be2e/6a170d086f7f0468a19148cc/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d2e815dd558f881/6a170d090e2e49076541a177/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" alt="" /><p>Resposta.</p>{
  "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"
}<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>{
  "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"
}<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>​​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"
      }
    }
  }
}<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>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": "...", ...}<h3>Verifique os dados</h3><p>Execute uma consulta para verificar:</p>GET hr-employees/_search<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc42f079c5e756057/6a170d0acf4f2501d9b2d1c7/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" alt="" /><h3>Construtor de Agentes de Acesso</h3><ol><li><p>Acesse a seção de <strong>Agentes.</strong></p></li><li><p>Clique em <strong>Criar um novo agente</strong></p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8e734817c5a7c6a/6a170d0ca929cf867cae0a34/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" alt="" /><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:

<strong>ID do agente:</strong> <code>hr_assistant</code></p><p><strong>Instruções personalizadas:</strong></p>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<p>
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>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.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23fb011e5b4f4d49/6a170d0e7d8d67f47a70e77f/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" alt="" /><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>Which employee is the one with the highest salary in the hr-employees index?<h4>Responder:</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0c52faacf63b583/6a170d0f0e2e497bfd41a17b/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte32a8a7e6363c7f2/6a170d115091680077e1bb44/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" alt="" /><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>
    <link>https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt664f490053e46e6b/6a170d13b0367d2d7e72bd84/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png" length="0" type="image/png"/>
    <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <content:encoded><![CDATA[<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 "Esquema da Solução".</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><p>Problema</p></li><li><p>Contexto</p></li><li><p>Código</p></li><li><p>Tags</p></li><li><p>Etapas da solução verificadas.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7bc35b6d54921e8/6a17f0176df73162760a0fe6/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" alt="Clicar em “Compartilhar solução” coletará os dados da sessão atual juntamente com os metadados relevantes." /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9967f52007fff99e/6a17f019ec0f8987c45a6701/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" alt="Os usuários fornecem contexto adicional por meio da interface web, e o JSON é então indexado no Elasticsearch." /><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><p>Correções classificadas e validadas pela comunidade</p></li><li><p>As mesmas instruções que originalmente resolveram o problema.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ba582f9872b65b/6a17f01b7f6f15c2d8c09c1c/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" alt="6 agentes de IA especializados do GOOGL que analisam o mercado" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86ed3bfe4b8bd2b/6a17f01c5ea30f868164b6ba/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" alt="Capacidades de análise de anomalias de volume e detecção de catástrofes dos agentes especializados do GOOGL." /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5af7c7c838308275/6a17f01e42022917b629f6ca/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" alt="Modelo unificado de “energia de mercado” de agentes especializados do GOOGL" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt775e1880aa7afacc/6a17f01f1d1b83ce1f93e528/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" alt="Como visualizar insights da análise de agentes especializados do GOOGL" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffe292009e446a70/6a17f0216df731068c0a0fea/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" alt="" /><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><p><strong>O Construtor de Agentes é fácil de usar:</strong></p></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><p><strong>Os LLMs se destacam em </strong>consultas<strong><code>kNN</code></strong><strong>, mas ainda precisam de orientação na geração de ES|QL:</strong></p></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><p><strong>Funções ES|QL exclusivas de snapshots vazaram para a documentação:</strong></p></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><p><strong>Ausência de orientações específicas para Serverless:</strong></p></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><p><strong>Valor dos eventos presenciais:</strong></p></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>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f079179be9832d4/6a17f023631730a69c585b6d/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <content:encoded><![CDATA[<h2>A2A e MCP: o código em ação</h2><p>Este artigo é um complemento ao artigo "Criando uma sala de imprensa com o agente LLM usando os protocolos A2A e MCP no Elasticsearch!", 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt232e466d2153c764/6a17f15f631730042d585b8d/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" alt="Fluxo de trabalho do agente de protocolo A2A e MCP" /><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>{
  "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"
    }
  }
}<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>{
  "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"
    }
  }
}<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>{
  "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
    }
  }
}<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># 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<p>O <strong>Agente de Arquivamento</strong> recebe dados históricos abrangentes do Agente A2A da Elastic e os retorna ao Reporter:</p>{
  "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
    }
  }
}<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># 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)<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>{
  "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
    }
  }
}<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># 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<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>{
  "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"
  }
}<p>O <strong>pesquisador</strong> verifica a alegação usando servidores de checagem de fatos do MCP e retorna informações atualizadas:</p>{
  "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"]
      }
    ]
  }
}<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>{
  "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
    }
  }
}<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># 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<p>O <strong>editor</strong> aprova o artigo e o encaminha:</p>{
  "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]"
  }
}<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># 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"]
    }<p>A <strong>editora</strong> confirma a publicação via A2A:</p>{
  "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
    }
  }
}<p>Segue abaixo a sequência completa do fluxo de trabalho A2A no repositório anexo, utilizando os mesmos agentes descritos acima.</p><p>#</p><p>De</p><p>Para</p><p>Ação</p><p>Protocolo</p><p>Descrição</p><p>1</p><p>Usuário</p><p>Chefe de Notícias</p><p>Atribuir história</p><p>HTTP POST</p><p>O usuário envia o tema e o enfoque da matéria.</p><p>2</p><p>Chefe de Notícias</p><p>Interno</p><p>Criar história</p><p>-</p><p>Cria um registro de história com um ID exclusivo.</p><p>3</p><p>Chefe de Notícias</p><p>Repórter</p><p>Atribuição de Delegado</p><p>A2A</p><p>Envia a atribuição da matéria através do protocolo A2A</p><p>4</p><p>Repórter</p><p>Interno</p><p>Aceitar tarefa</p><p>-</p><p>Atribuição de estoques internamente</p><p>5</p><p>Repórter</p><p>Servidor MCP</p><p>Gerar esboço</p><p>MCP/HTTP</p><p>Cria o esboço do artigo e as perguntas de pesquisa.</p><p>6a</p><p>Repórter</p><p>Pesquisador</p><p>Solicitar pesquisa</p><p>A2A</p><p>Envia perguntas (paralelo com 6b)</p><p>6b</p><p>Repórter</p><p>Arquivista</p><p>Pesquisar no arquivo</p><p>A2A JSONRPC</p><p>Pesquisa artigos históricos (paralelo com 6a)</p><p>7</p><p>Pesquisador</p><p>Servidor MCP</p><p>Questões de pesquisa</p><p>MCP/HTTP</p><p>Utiliza a abordagem antropogênica via MCP para responder a perguntas.</p><p>8</p><p>Pesquisador</p><p>Repórter</p><p>Retornar à pesquisa</p><p>A2A</p><p>Devolve respostas de pesquisa</p><p>9</p><p>Arquivista</p><p>Elasticsearch</p><p>Índice de pesquisa</p><p>API REST do ES</p><p>Consultas ao índice news_archive</p><p>10</p><p>Arquivista</p><p>Repórter</p><p>Retornar ao arquivo</p><p>A2A JSONRPC</p><p>Retorna resultados de pesquisa históricos</p><p>11</p><p>Repórter</p><p>Servidor MCP</p><p>Gerar artigo</p><p>MCP/HTTP</p><p>Cria artigo com contexto de pesquisa/arquivo</p><p>12</p><p>Repórter</p><p>Interno</p><p>Rascunho da loja</p><p>-</p><p>Salva o rascunho internamente</p><p>13</p><p>Repórter</p><p>Chefe de Notícias</p><p>Enviar rascunho</p><p>A2A</p><p>Entrega a versão finalizada</p><p>14</p><p>Chefe de Notícias</p><p>Interno</p><p>Atualização da história</p><p>-</p><p>Armazena o rascunho e atualiza o status para "rascunho_enviado".</p><p>15</p><p>Chefe de Notícias</p><p>Editor</p><p>Revisão do rascunho</p><p>A2A</p><p>Encaminha automaticamente para o Editor para revisão.</p><p>16</p><p>Editor</p><p>Servidor MCP</p><p>Artigo de revisão</p><p>MCP/HTTP</p><p>Analisa conteúdo usando Anthropic via MCP.</p><p>17</p><p>Editor</p><p>Chefe de Notícias</p><p>Revisão de retorno</p><p>A2A</p><p>Envia comentários e sugestões editoriais.</p><p>18</p><p>Chefe de Notícias</p><p>Interno</p><p>Avaliação da loja</p><p>-</p><p>Feedback do editor de lojas</p><p>19</p><p>Chefe de Notícias</p><p>Repórter</p><p>Aplicar edições</p><p>A2A</p><p>Feedback da revisão de rotas para o repórter</p><p>20</p><p>Repórter</p><p>Servidor MCP</p><p>Aplicar edições</p><p>MCP/HTTP</p><p>Revisa o artigo com base no feedback.</p><p>21</p><p>Repórter</p><p>Interno</p><p>Rascunho atualizado</p><p>-</p><p>Atualiza a versão preliminar com revisões.</p><p>22</p><p>Repórter</p><p>Chefe de Notícias</p><p>Devolução revisada</p><p>A2A</p><p>Devolve artigo revisado</p><p>23</p><p>Chefe de Notícias</p><p>Interno</p><p>Atualização da história</p><p>-</p><p>Lojas revisaram a versão preliminar, status para "revisado"</p><p>24</p><p>Chefe de Notícias</p><p>Editor</p><p>Publicar artigo</p><p>A2A</p><p>Rotas automáticas para o editor</p><p>25</p><p>Editor</p><p>Servidor MCP</p><p>Gerar etiquetas</p><p>MCP/HTTP</p><p>Cria etiquetas e categorias</p><p>26</p><p>Editor</p><p>Elasticsearch</p><p>Artigo de índice</p><p>API REST do ES</p><p>Indexa o artigo ao índice news_archive</p><p>27</p><p>Editor</p><p>Sistema de arquivos</p><p>Salvar Markdown</p><p>Entrada/Saída de Arquivos</p><p>Salva o artigo como .md arquivo em /artigos</p><p>28</p><p>Editor</p><p>Chefe de Notícias</p><p>Confirmar publicação</p><p>A2A</p><p>Retorna o status de sucesso</p><p>29</p><p>Chefe de Notícias</p><p>Interno</p><p>Atualização da história</p><p>-</p><p>Atualiza o status da matéria para "publicada".</p><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><p><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></p></li><li><p><strong>Especificação A2A</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p></li><li><p><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></p></li><li><p><strong>Protocolo de Contexto do Modelo</strong>: <a href="https://modelcontextprotocol.io/">https://modelcontextprotocol.io</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b1f22cdc2130333/6a17f161ec0f8917fa5a6712/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <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 ("deixe-me pesquisar isso no Google..."), 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 <em><strong>nós mesmos</strong></em> é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 "catar e digitar", 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 "explique isso para mim como se eu tivesse cinco anos de idade" 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 "janela de contexto". 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfa000ccfdd9d184/6a17ddb57b54f955f38b37da/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" alt="Como os LLMs processam as consultas dos usuários e criam contexto" /><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 "<a href="https://research.trychroma.com/context-rot">deterioração de contexto</a> ") afeta a qualidade e o desempenho das operações de IA generativa; ele impacta significativamente o "orçamento de atenção" do LLM (sua memória de trabalho) e dilui a relevância entre muitos tokens concorrentes. O conceito de "deterioração do contexto" 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 "lixo entra, lixo sai": 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 "legada"?) 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 "lógica" 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 "raciocínio" 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 'engenharia de prompts' - 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 " <a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">engenharia de contexto</a>" estende as técnicas de "engenharia de prompts" 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><p><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 "orçamento de atenção" 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.</p></li><li><p><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. </p></li><li><p><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.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e1e68c08fe050bc/6a17ddb7414c645035945073/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" alt="Engenharia de contexto em mestrados em direito" /><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><p><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 "pensar passo a passo" ou "respirar fundo" 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.</p></li><li><p><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. </p></li><li><p><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. </p></li><li><p><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.</p></li><li><p><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. </p></li><li><p><strong>Geração Aumentada por Recuperação (RAG)</strong> - Eu realmente gosto da descrição de RAG como "integração dinâmica de conhecimento". 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.</p></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 "boca" 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 "<a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai">colapso de contexto</a> ", 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 "tradutor universal" 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 "incompatibilidades de impedância" 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>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f98889141fba45b/6a17ddb80b0bed0822dd34a2/79c0378b68d74d9e018c35ee2c1fd17daeee9f2c-1080x608.webp" length="0" type="image/webp"/>
    <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>Python 13.12 - Linguagem principal de backend</p></li><li><p>Node.js 18+ - Interface de usuário React opcional</p></li></ul><p>Frameworks principais e SDKs:</p><ul><li><p>SDK A2A 0.3.8 - Coordenação e comunicação de agentes</p></li><li><p>SDK Antrópico - Integração com Claude para geração de IA</p></li><li><p>Uvicorn - Servidor ASGI para executar agentes</p></li><li><p>FastMCP 2.12.5+ - Implementação do servidor MCP</p></li><li><p>React 18.2 - Framework de interface de usuário para front-end</p></li></ul><p>Dados e pesquisa</p><ul><li><p>Elasticsearch 9.1.1+ - Indexação e pesquisa de artigos</p></li></ul><p>Implantação do Docker (opcional, mas recomendada)</p><ul><li><p>Docker 28.5.1+</p></li></ul><h2>Seção 1: O que é Agent2Agent (A2A)?</h2><h3>Definição e conceitos fundamentais</h3><p>Agent2Agent (A2A) é um protocolo padronizado para interação entre agentes LLM independentes. Em vez de um único sistema monolítico que lida com todas as tarefas, o A2A permite que vários agentes especializados se comuniquem, coordenem e colaborem para realizar fluxos de trabalho complexos que seriam difíceis, lentos ou simplesmente impossíveis de serem gerenciados com eficiência por um único agente.</p><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 "prosumidores", 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><p><strong>Troca de mensagens</strong>: Os agentes comunicam-se por meio de mensagens estruturadas com propriedades e formatos bem definidos.</p></li><li><p><strong>Coordenação</strong>: Os agentes orquestram fluxos de trabalho complexos, delegando tarefas uns aos outros e gerenciando dependências sem bloquear outros agentes.</p></li><li><p><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.</p></li><li><p><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).</p></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><p><strong>Chefe de Notícias</strong> (coordenador/cliente): Atribui pautas e supervisiona o fluxo de trabalho.</p></li><li><p><strong>Agente de reportagem</strong>: Redige artigos com base em pesquisas e entrevistas.</p></li><li><p><strong>Agente de Pesquisa</strong>: Reúne fatos, estatísticas e informações de contexto.</p></li><li><p><strong>Agente de Arquivo</strong>: Pesquisa artigos históricos e identifica tendências usando o Elasticsearch.</p></li><li><p><strong>Agente Editorial</strong>: Analisa artigos quanto à qualidade, estilo e otimização para SEO.</p></li><li><p><strong>Agente de Publicação</strong>: Publica artigos aprovados na plataforma do blog via CI/CD</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6c7215a96326481/6a17f2dd445de953024d0243/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" alt="" /><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, "Archie Archivist". Note que fornecedores de software como a Elastic hospedam seus agentes A2A e fornecem um URL para acesso:</p>{
  "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"]
}<p>Este cartão de agente revela vários aspectos importantes do agente de arquivamento da Elastic. O agente se identifica como "Archie Archivist" 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>{
  "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"
    }
  }
}<p>Esta mensagem de tarefa de exemplo demonstra vários aspectos importantes da comunicação A2A.</p><ul><li><p>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.</p></li><li><p>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.</p></li><li><p>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.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74d59cd9267f54d8/6a17f2de505ac31129ad8c71/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" alt="" /><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><p><strong>Clientes:</strong> aplicativos (como o Claude Desktop ou aplicativos de IA personalizados) que se conectam aos servidores MCP para acessar suas funcionalidades.</p></li><li><p><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.</p><ul><li><p><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.</p></li><li><p><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).</p></li><li><p><strong>Instruções: </strong>modelos de instruções reutilizáveis com variáveis que orientam o modelo na realização de tarefas específicas.</p></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><p><strong>O Agente de Pesquisa</strong> utiliza:</p><ul><li><p>Servidor MCP da API de notícias (acesso a bancos de dados de notícias)</p></li><li><p>Servidor MCP de verificação de fatos (verifica alegações em fontes confiáveis)</p></li><li><p>Servidor MCP de base de dados académica (artigos académicos e investigação)</p></li></ul></li><li><p><strong>O agente repórter</strong> utiliza:</p><ul><li><p>Guia de Estilo do Servidor MCP (padrões de redação para redações)</p></li><li><p>Servidor MCP de modelos (modelos e formatos de artigos)</p></li><li><p>Servidor MCP da Biblioteca de Imagens (fotos e gráficos de banco de imagens)</p></li></ul></li><li><p><strong>O Editor Agent</strong> utiliza:</p><ul><li><p>Servidor MCP do Verificador Gramatical (ferramentas de qualidade linguística)</p></li><li><p>Servidor MCP de Detecção de Plágio (verificação de originalidade)</p></li><li><p>Servidor MCP de Análise de SEO (otimização de títulos e palavras-chave)</p></li></ul></li><li><p><strong>O Publisher Agent</strong> utiliza:</p><ul><li><p>Servidor CMS MCP (API do sistema de gerenciamento de conteúdo)</p></li><li><p>Servidor CI/CD MCP (pipeline de implantação)</p></li><li><p>Servidor MCP de análise (rastreamento e monitoramento)</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt195fe0bd36d36a48/6a17f2e0b1e113afe479f36c/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" alt="" /><h2>
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><p>Recurso</p><p>Agente para Agente (A2A)</p><p>Protocolo de Contexto do Modelo (MCP)</p><p>Híbrido (A2A + MCP)</p><p>Objetivo principal</p><p>Coordenação multiagente: Permite que uma equipe de agentes especializados trabalhe em conjunto em fluxos de trabalho complexos e com várias etapas.</p><p>Aprimoramento para agente único: Amplia a capacidade de um único LLM/Agente com ferramentas, recursos e dados externos.</p><p>Força combinada: A2A gerencia o fluxo de trabalho da equipe, enquanto a MCP fornece ferramentas para cada membro da equipe.</p><p>Exemplo de equipe de redação</p><p>A cadeia de fluxo de trabalho: Chefe de Notícias → Repórter → Pesquisador → Editor → Publicador. Esta é a camada de coordenação.</p><p>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.</p><p>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.</p><p>Quando usar qual</p><p>Quando você precisa de colaboração genuína, iteração e aprimoramento, ou de conhecimento especializado dividido entre vários agentes.</p><p>Quando um único agente precisa acessar várias ferramentas e fontes de dados ou requer integração padronizada com sistemas proprietários.</p><p>Quando você precisa dos benefícios organizacionais dos sistemas multiagentes e dos benefícios de padronização e ecossistema do MCP.</p><p>Benefício principal</p><p>Autonomia e escalabilidade: Os agentes podem tomar decisões independentes e o sistema permite a escalabilidade horizontal de funções especializadas.</p><p>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.</p><p>Separação clara de responsabilidades: torna o sistema mais fácil de entender: A2A = trabalho em equipe, MCP = acesso à ferramenta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1735ea5de41e10fd/6a17f2e26864a4125cb688c4/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" alt="" /><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>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2716d804698ec878/6a17f2e41480095fd7b48888/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png" length="0" type="image/png"/>
    <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>(obrigatório) <code>max_chunk_size</code>: O número máximo de palavras em um bloco.</p></li><li><p>Qualquer uma das seguintes opções:</p><ul><li><p><code>separators</code>Uma lista de padrões de strings de expressão regular que serão usados para dividir o documento em partes.</p></li><li><p><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.</p></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><p>Se o documento de entrada já estiver dentro do tamanho máximo do bloco, retorne um único bloco que abranja toda a entrada.</p></li><li><p>Divida o texto em partes potenciais com base nas ocorrências do separador. Para cada bloco potencial:</p><ol><li><p>Se o fragmento em potencial estiver dentro do tamanho máximo permitido, adicione-o à lista de fragmentos a serem retornados ao usuário.</p></li><li><p>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.</p></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>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separator_group": "plaintext"
}<p>Isso lhe dará uma estratégia de fragmentação recursiva que utiliza a lista de separadores <code>["(?&lt;!\\n)\\n\\n(?!\\n)", "(?&lt;!\\n)\\n(?!\\n)")]</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>[
"\n# ",
       "\n## ",
       "\n### ",
       "\n#### ",
       "\n##### ",
       "\n###### ",
       "\n^(?!\\s*$).*\\n-{1,}\\n",
       "\n^(?!\\s*$).*\\n={1,}\\n"
]<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>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n\n", "\n", "&lt;my-custom-separator&gt;"]
}<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>"chunking_settings": {
    "strategy": "recursive",
    "max_chunk_size": 25,
    "separators": ["\n# ", "\n## "]
}<p>Vamos analisar um documento Markdown simples, sem divisões em partes (unchunked):</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb5f41d1bd43ba50/6a17e831e9ea87c1d8a9c5f3/3a5507f4a1288065097231548e5b18e240508785-1302x1446.png" alt="Um documento Markdown não dividido em partes" /><p>Agora vamos usar as configurações de fragmentação definidas acima para dividir o documento em partes:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfffda162c7b9c87a/6a17e83296142aefa8eb1b0b/a3313c4c40ff39b8dbcdd7c4878c723f088e6c1a-1600x1187.png" alt="Dividindo um documento em partes no Elasticsearch" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt96f65346a8e09e3a/6a17e834445de9157b4d015e/79a2921943191ea631df94c9d465818ec8d3e738-1600x1206.png" alt="Dividir um documento usando o segundo separador - fragmentação de um documento no Elasticsearch" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28381c8f85aedf07/6a17e836ec0f89801e5a6640/459e695cce7540267422396b9a62ff4ad35f61db-1600x1260.png" alt="Blocos finais em um documento após o agrupamento baseado em frases no Elasticsearch" /><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 como configurar as definições de fragmentação.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/recursive-chunking-structured-documents-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/recursive-chunking-structured-documents-elasticsearch</guid>
    <category><![CDATA[Noções básicas]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Daniel Rubinstein]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf442dc4941f37be7/6a17e838505ac3eaf8ad8b3d/591872e31880768ca927507654a621addc0d124d-1600x960.png" length="0" type="image/png"/>
    <pubDate>Tue, 11 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Busca multimodal de picos de montanhas com Elasticsearch e SigLIP-2 ]]></title>
    <description><![CDATA[Aprenda como implementar buscas multimodais de texto para imagem e de imagem para imagem usando embeddings SigLIP-2 e busca vetorial kNN do Elasticsearch. Objetivo do projeto: encontrar fotos do pico do Monte Ama Dablam tiradas durante uma trilha no Everest.]]></description>
    <content:encoded><![CDATA[<p>Você já quis pesquisar seu álbum de fotos por significado? Experimente buscas como "mostre-me fotos minhas onde estou usando uma jaqueta azul e sentado em um banco", "mostre-me fotos do Monte Everest" ou "saquê e sushi". Pegue uma xícara de café (ou sua bebida favorita) e continue lendo. Neste blog, mostraremos como criar um aplicativo de busca híbrido multimodal. Multimodal significa que o aplicativo consegue entender e pesquisar em diferentes tipos de entrada — texto, imagens e áudio — e não apenas palavras. Híbrido significa que combina técnicas como correspondência de palavras-chave, busca vetorial kNN e geofencing para fornecer resultados mais precisos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfa1ec1ccd450e94/6a17da751d1b8308ee93e344/0ec6bbb45013846b59ee00d2bf73ee2182ee7392-1920x1080.gif" alt="Biblioteca de fotos de diferentes picos de montanhas da trilha até o Monte Everest." /><p>Para isso, utilizamos o SigLIP-2 do Google para gerar representações vetoriais tanto para imagens quanto para texto, e as armazenamos no banco de dados vetorial Elasticsearch. No momento da consulta, convertemos a entrada da pesquisa, seja texto ou imagem, em representações vetoriais (embeddings) e executamos buscas vetoriais kNN rápidas para recuperar os resultados. Essa configuração permite uma busca eficiente de texto para imagem e de imagem para imagem. A interface Streamlit UI dá vida a este projeto, fornecendo-nos um frontend que não só permite realizar buscas textuais para encontrar e visualizar as fotos correspondentes no álbum, como também nos permite identificar o pico da montanha na imagem carregada e visualizar outras fotos dessa montanha no álbum.
Abordamos também as medidas que tomamos para melhorar a precisão da pesquisa, juntamente com dicas e truques práticos. Para uma exploração mais aprofundada, disponibilizamos um <a href="https://github.com/navneet83/multimodal-mountain-peak-search">repositório no GitHub</a> e um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">notebook no Colab</a>.</p><h2>Como tudo começou</h2><p>Este post do blog foi inspirado por uma criança de 10 anos que me pediu para mostrar todas as fotos do Monte Ama Dablam que tirei na minha trilha até o Acampamento Base do Everest. Enquanto examinávamos o álbum de fotos, também me pediram para identificar vários outros picos de montanhas, alguns dos quais eu não sabia o nome.</p><p>Isso me deu a ideia de que este pode ser um projeto divertido de visão computacional. O que queríamos alcançar:</p><ul><li><p>Encontre fotos de um pico de montanha pelo nome.</p></li><li><p>Adivinhe o nome do pico da montanha a partir de uma imagem e encontre picos semelhantes no álbum de fotos.</p></li><li><p>Fazer com que as consultas de conceito funcionem (<em>pessoa</em>, <em>rio</em>, <em>bandeiras de oração</em>, <em>etc.)</em></p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf82df9d7005fc3fe/6a17da78abe0f2e77bdfe8b9/e9d0d720a9b565d5b749bdc915068852d4f157ad-1200x1600.png" alt="Monte Ama Dablam " /><h2>Montando a equipe dos sonhos: SigLIP-2, Elasticsearch e Streamlit</h2><p>Rapidamente ficou claro que, para isso funcionar, precisaríamos transformar tanto o texto (“Ama Dablam”) quanto as imagens (fotos do meu álbum) em vetores que pudessem ser comparados de forma significativa, ou seja, no mesmo espaço vetorial. Uma vez feito isso, a busca se torna simplesmente "encontrar os vizinhos mais próximos".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f80b69a9d5bd28a/6a17da7a4b055ddd1243209e/20e6f8b7d4fa48414f407ec200adbe00ee28d517-1536x1024.png" alt="SigLIP-2, Elasticsearch e Streamlit - uma combinação dos sonhos." /><p>Para gerar representações vetoriais de imagens, usamos um<a href="https://huggingface.co/blog/vlms-2025"> codificador multilíngue de visão e linguagem</a>, de modo que uma foto de uma montanha e uma frase como "Ama Dablam" fiquem no mesmo espaço vetorial.</p><p><a href="https://huggingface.co/blog/siglip2"><strong>O SigLIP-2</strong></a>, lançado recentemente pelo Google, se encaixa bem aqui. Ele consegue gerar embeddings sem treinamento específico para a tarefa (uma configuração <strong>zero-shot</strong> ) e funciona bem para o nosso caso de uso: fotos não rotuladas e picos com nomes e idiomas diferentes. Como foi treinado para correspondência de texto ↔ imagem, uma foto da montanha tirada durante a trilha e um breve texto de exemplo resultam em representações vetoriais muito semelhantes, mesmo quando o idioma ou a ortografia da consulta variam.</p><p>O SigLIP-2 oferece um excelente equilíbrio entre qualidade e velocidade, suporta múltiplas resoluções de entrada e funciona tanto na CPU quanto na GPU. O SigLIP-2 foi projetado para ser mais resistente a fotos tiradas ao ar livre em comparação com modelos anteriores, como o CLIP original. Durante nossos testes, o SigLIP-2 gerou resultados confiáveis de forma consistente. Além disso, conta com amplo suporte, o que a torna a escolha óbvia para este projeto.</p><p>Em seguida, precisamos de um banco de dados vetorial para armazenar os embeddings e realizar buscas avançadas. Deveria suportar não apenas a busca kNN de cosseno em embeddings de imagem, mas também aplicar filtros de geolocalização e texto em uma única consulta. O Elasticsearch se encaixa bem aqui: ele lida muito bem com vetores (HNSW kNN em campos dense_vector), suporta busca híbrida que combina consultas de texto, vetores e geolocalização, e oferece filtragem e classificação prontas para uso. Ele também se adapta à escala horizontal, facilitando a expansão de um punhado de fotos para milhares. O <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/python">cliente oficial do Elasticsearch para Python</a> mantém a infraestrutura simples e se integra perfeitamente ao projeto. Por fim, precisamos de uma interface leve onde possamos inserir consultas de pesquisa e visualizar os resultados. Para uma demonstração rápida baseada em Python, o Streamlit é uma ótima opção. Ele fornece os recursos básicos de que precisamos: upload de arquivos, uma grade de imagens responsiva e menus suspensos para classificação e geolocalização. É fácil clonar e executar localmente, e também funciona em um notebook do Colab.</p><h2>Implementação</h2><h3>Design e estratégia de indexação do Elasticsearch</h3><p>Usaremos dois índices para este projeto: <code>peaks_catalog</code> e <code>photos</code>.</p><h4>Índice do catálogo de picos</h4><p>Este índice serve como um catálogo compacto dos picos de montanhas mais proeminentes que podem ser vistos durante a trilha até o Acampamento Base do Everest. Cada documento neste índice corresponde a um único pico de montanha, como o Monte Everest. Para cada documento de pico de montanha, armazenamos nomes/apelidos, coordenadas opcionais de latitude e longitude e um único vetor protótipo construído pela combinação de prompts de texto SigLIP-2 (e imagens de referência opcionais).</p><p><strong>Mapeamento do índice:</strong></p><p>Campo</p><p>Tipo</p><p>Exemplo</p><p>Objetivo/Observações</p><p>Vetor/Indexação</p><p>eu ia</p><p>palavra-chave</p><p>ama-dablam</p><p>Slug/ID estável</p><p>—</p><p>nomes</p><p>texto + subcampo de palavra-chave</p><p>["Ama Dablam","Amadablam"]</p><p>Aliases / nomes multilíngues; names.raw para filtros exatos</p><p>—</p><p>latlon</p><p>ponto_geográfico</p><p>{"lat":27.8617,"lon":86.8614}</p><p>Coordenadas GPS do pico como uma combinação de latitude/longitude (opcional)</p><p>—</p><p>elev_m</p><p>inteiro</p><p>6812</p><p>Elevação (opcional)</p><p>—</p><p>texto incorporado</p><p>dense_vector</p><p>768</p><p>Protótipo misto (com instruções e, opcionalmente, 1 a 3 imagens de referência) para este pico.</p><p>índice:true, similaridade:"cosseno", opções_de_índice:{type:"hnsw", m:16, ef_construction:128}</p><p>Este índice é usado principalmente para buscas de imagem para imagem, como identificar picos de montanhas a partir de imagens. Também utilizamos esse índice para aprimorar os resultados de busca de texto para imagem.</p><p>Em resumo, o <code>peaks_catalog</code> transforma a pergunta "Que montanha é esta?" em um problema de vizinho mais próximo focado, separando efetivamente a compreensão conceitual das complexidades dos dados da imagem.</p><p><strong>Estratégia de indexação para o índice peaks_catalog: </strong>Começamos criando uma lista dos picos mais proeminentes visíveis durante a trilha do Campo Base do Everest. Para cada pico, armazenamos sua localização geográfica, nome, sinônimos e altitude em um <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/data/peaks.yaml">arquivo YAML</a>. O próximo passo é <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L351">gerar o embedding</a> para cada pico e armazená-lo no campo <code>text_embed</code> . Para gerar embeddings robustos, utilizamos a seguinte técnica:</p><ul><li><p>Crie um protótipo de texto usando:</p><ul><li><p>nomes dos picos</p></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L301">Conjunto de prompts</a> (usando vários prompts diferentes para tentar responder à mesma pergunta), por exemplo:</p><ul><li><p>“uma foto natural do pico da montanha {name} no Himalaia, Nepal”</p></li><li><p>“{name} pico emblemático na região de Khumbu, paisagem alpina”</p></li><li><p>“{name} cume da montanha, neve, crista rochosa”</p></li></ul></li><li><p><a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L333">Anti-conceito</a> opcional (indicando ao SigLIP-2 o que não deve ser correspondido): subtrair um pequeno vetor para "pintura, ilustração, pôster, mapa, logotipo" para que haja uma preferência por fotos reais.</p></li></ul></li><li><p>Opcionalmente, <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L388C13-L388C29">crie um protótipo de imagem</a> se forem fornecidas imagens de referência do pico.</p></li></ul><p>Em seguida, <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L392">combinamos o texto e o protótipo da imagem</a> para gerar a incorporação final. Finalmente, o documento é <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/scripts/embed_and_index_photos.py#L396">indexado</a> com todos os campos necessários:</p>def l2norm(v: np.ndarray) -&gt; np.ndarray:
    return v / (np.linalg.norm(v) + 1e-12)
def compute_blended_peak_vec(
        emb: Siglip2,
        names: List[str],
        peak_id: str,
        peaks_images_root: str,
        alpha_text: float = 0.5,
        max_images: int = 3,
) -&gt; Tuple[np.ndarray, int, int, List[str]]:
    """
    Build blended vector for a single peak.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


Response (first two documents):
{
 "hits": {
   "total": {
     "value": 56,
     "relation": "eq"
   },
   "max_score": 0.5779596,
   "hits": [
     {
       "_index": "photos",
       "_id": "d01da3a1141981486c3493f6053c79e92a788463",
       "_score": 0.5779596,
       "_source": {
         "path": "IMG_2738.HEIC",
         "predicted_peaks": [
           "Pumori",
           "Kyajo Ri",
           "Khumbila",
           "Nangkartshang",
           "Kongde Ri"
         ],
         "gps": {
           "lat": 27.97116388888889,
           "lon": 86.82331111111111
         },
         "shot_time": "2023-11-03T08:07:13"
       }
     },
     {
       "_index": "photos",
       "_id": "c79d251f07adc5efaedc53561110a7fd78e23914",
       "_score": 0.5766071,
       "_source": {
         "path": "IMG_2761.HEIC",
         "predicted_peaks": [
           "Kyajo Ri",
           "Makalu",
           "Baruntse",
           "Cho Oyu",
           "Khumbila"
         ],
         "gps": {
           "lat": 27.975558333333332,
           "lon": 86.82515
         },
         "shot_time": "2023-11-03T08:51:08"
       }
     }
}<h2>Interface de usuário Streamlit</h2><p>Para integrar tudo, criamos uma interface de usuário Streamlit simples que nos permite executar ambos os casos de uso de pesquisa. A barra lateral esquerda exibe uma lista rolável de picos (agregados de <code>photos.predicted_peaks</code>) com caixas de seleção e um minimapa/filtro geográfico. Na parte superior, há uma caixa <strong>de pesquisa por nome</strong> e um botão <strong>para identificar o usuário a partir de uma foto</strong> enviada. O painel central apresenta uma grade de miniaturas interativa que exibe as pontuações kNN, os indicadores de pico previsto e os horários de captura. Cada imagem inclui um botão <strong>"Ver imagem"</strong> para pré-visualizações em resolução total.</p><p><strong>Pesquisa por upload de imagem:</strong> Prevemos o pico e encontramos picos correspondentes no álbum de fotos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1fb2b0304a310d2/6a17da8425daab7cda08a0fa/dca540cbf5279e6d6102c5a0c0351ddd4ac91cda-1600x1112.png" alt="Uma interface de usuário simples e intuitiva que permite a busca multimodal por texto para imagem e por imagem para os picos do Monte Ama Dablam." /><p><strong>Pesquisa por texto</strong>: Encontre os picos correspondentes no álbum a partir do texto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt496c1ae8f7886320/6a17da86abe0f2da48dfe8bd/b1e8618db746cd49ea4962d3dc73031387b975dd-1600x1166.png" alt="Como pesquisar um pico do Monte Everest usando a busca por texto na biblioteca de picos de montanhas." /><h2>Conclusão</h2><p>Tudo começou com <em>uma pergunta: "Podemos ver as </em><em> fotos</em><em><strong>do Ama Dablam ?"</strong></em> transformou-se em um pequeno sistema <strong>de busca multimodal</strong> funcional. Capturamos fotos brutas da trilha, transformamos em <strong>embeddings SigLIP-2</strong> e usamos <strong>o Elasticsearch</strong> para realizar uma rápida <strong>análise kNN</strong> sobre vetores, além de filtros geográficos/temporais simples para exibir as imagens <em>relevantes</em>. Ao longo do processo, separamos as preocupações com dois índices: um pequeno <code>peaks_catalog</code> de protótipos combinados (para identificação) e um índice escalável <code>photos</code> de vetores de imagem e EXIF (para recuperação). É prático, reproduzível e fácil de expandir.</p><p>Se você quiser ajustá-lo, existem algumas configurações que você pode modificar:</p><ul><li><p><strong>Configurações de tempo de consulta:</strong> <code>k</code> (quantos vizinhos você deseja retornar) e <code>num_candidates</code> (quão ampla a pesquisa antes da pontuação final). Essas configurações são discutidas no blog <a href="https://www.elastic.co/search-labs/blog/elasticsearch-knn-and-num-candidates-strategies">aqui</a>.</p></li><li><p><strong>Configurações de tempo de indexação:</strong> <code>m</code> (conectividade do grafo) e <code>ef_construction</code> (precisão do tempo de construção vs. memória). Para consultas, experimente também com <code>ef_search</code> — um valor maior geralmente significa melhor recuperação com alguma compensação de latência. Consulte <a href="https://www.elastic.co/search-labs/blog/hnsw-graph">este blog</a> para obter mais detalhes sobre essas configurações.</p></li></ul><p>Olhando para o futuro, modelos/reclassificadores nativos para busca <strong>multimodal</strong> e <strong>multilíngue</strong> chegarão em breve ao ecossistema Elastic, o que deverá tornar a recuperação de imagens/texto e a classificação híbrida ainda mais robustas e prontas para uso.<a href="https://ir.elastic.co/news/news-details/2025/Elastic-Completes-Acquisition-of-Jina-AI-a-Leader-in-Frontier-Models-for-Multimodal-and-Multilingual-Search/default.aspx?utm_source=chatgpt.com"> ir.elastic.co+1</a></p><p>Se você quiser experimentar você mesmo:</p><ul><li><p><strong>Repositório do GitHub:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search"><em>https://github.com/navneet83/multimodal-mountain-peak-search</em></a></p></li><li><p><strong>Guia rápido do Colab:</strong> <a href="https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb">https://github.com/navneet83/multimodal-mountain-peak-search/blob/main/notebooks/multimodal_mountain_peak_search.ipynb</a></p></li></ul><p>Com isso, nossa jornada chegou ao fim e é hora de voltar para casa. Espero que isso tenha sido útil e, se você quebrar alguma coisa (ou melhorar alguma coisa), adoraria saber o que você mudou.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdce2fff1569d2a8b/6a17da894b055dd1f24320a2/d324d1e1472f1bfbd8f25747f57bdeeb9c7f16b2-1600x1200.png" alt="" />]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/multimodal-search-siglip-2-elasticsearch</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[Busca híbrida]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Python]]></category>
    <dc:creator><![CDATA[Navneet Kumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltccb66279debb05f9/6a17da8b63baffe228741b15/ffcf93358a7c5dadcea82faf3de460bf060d003c-1600x1200.png" length="0" type="image/png"/>
    <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[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>
    <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><p><strong>Use </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server"><strong>o MCP</strong></a> para aprimorar seu agente de IA (como no <strong>Cursor</strong> ou <strong>no VS Code</strong>) com ferramentas especializadas. Trata-se da abordagem "traga suas próprias ferramentas", 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.</p></li><li><p><strong>Utilize o </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"><strong>protocolo A2A</strong></a> 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.</p></li><li><p><strong>Utilize </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api"><strong>as APIs do Agent Builder</strong></a> para obter controle programático completo ao criar um aplicativo personalizado do zero.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt011118f0a9279185/6a17f367dbb4ffc4f3fb581a/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" alt="Criando uma ferramenta de base de conhecimento personalizada no Agent Builder." /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd0e62ae0f394c3d/6a17f368e317916ec32d5933/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" alt="Como conectar o cursor da interface de ferramentas do Kibana ao servidor Elastic MCP." /><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>{
  "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 &lt;ELASTIC_API_KEY&gt;"
      }
    }
  }
}<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2837638263e628ed/6a17f36adbb4ffeb9cfb5820/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" alt="Imagem da ferramenta de servidor Elastic Agent Builder MCP disponível no Cursor." /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83fa357261b30e93/6a17f36c4b055d16d1432326/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" alt="Agentes de cursor invocam ferramentas personalizadas para responder a perguntas e orientar o processo de geração de código." /><p>Nos bastidores, a magia acontece:</p><ol><li><p>O agente Cursor decide a melhor forma de responder à sua pergunta e, em seguida, decide ligar para o <code>engineering_documentation_internal_search</code></p></li><li><p>Ela invoca a ferramenta com uma consulta em linguagem natural.</p></li><li><p>A ferramenta executa uma busca semântica no índice <code>elastic-dev-docs</code> e retorna os procedimentos mais relevantes e atualizados.</p></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><p>Escolher o protocolo certo para as suas necessidades (MCP).</p></li><li><p>Criação de uma ferramenta de base de conhecimento personalizada.</p></li><li><p>Conectar essa ferramenta a um assistente de IDE como o Cursor.</p></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>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Ferramentas de IA ]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5b61961b6269ab1/6a17f36ea29299d839d02db2/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>Como podemos estimar a qualidade das respostas dessas aplicações de IA?</p></li><li><p>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?</p></li><li><p>Como podemos testar esses resultados de forma fácil e repetível?</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb70932abe8d4de75/6a17f04ea292990c52d02d61/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b9dbede85a56bd6/6a17f050e8fbce88943a1a30/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" alt="" /><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: "Se incorporarmos essa alteração, podemos garantir que ela representa uma melhoria real e não prejudicará a experiência do usuário?"</p><p>A maioria dos experimentos que realizamos geralmente inclui:</p><ul><li><p><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.”</p></li><li><p><strong>Critérios de sucesso:</strong> Limiares claros que definem o que significa "sucesso". <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.”</p></li><li><p><strong>Plano de avaliação:</strong> Como medimos o sucesso (métricas, conjuntos de dados, método de comparação)</p></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><p>Etapa 1: Identificar o problema</p></li><li><p>Etapa 2: Definir métricas</p></li><li><p>Etapa 3: Formule uma hipótese clara</p></li><li><p>Etapa 4: Preparar o conjunto de dados de avaliação</p></li><li><p>Etapa 5: Execute o experimento</p></li><li><p>Etapa 6: Analisar resultados + iterar</p></li><li><p>Etapa 7: Tome uma decisão e documente-a.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06bfe2f0e4205a18/6a17f052faa91358eb93c968/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" alt="" /><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><p>Métrica</p><p>Descrição</p><p>Precisão e memorização</p><p>Compare os IDs de alerta entre as saídas reais e esperadas para medir a precisão da detecção.</p><p>Semelhança</p><p>Utilize o BERTScore para comparar a similaridade semântica do texto de resposta.</p><p>Factualidade</p><p>Os principais indicadores de comprometimento (IOCs) estão presentes? As táticas MITRE (taxonomia de ataques do setor) estão corretamente representadas?</p><p>Consistência da cadeia de ataque</p><p>Compare o número de descobertas para verificar se houve superestimação ou subestimação da notificação do ataque.</p><p><strong>Agente de IA Elástico</strong></p><p>Métrica</p><p>Descrição</p><p>Precisão e memorização</p><p>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.</p><p>Factualidade</p><p>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?</p><p>Relevância da resposta</p><p>A resposta contém informações periféricas ou não relacionadas à consulta do usuário?</p><p>Completude da resposta</p><p>A resposta atende a todas as partes da consulta do usuário? A resposta contém todas as informações presentes na verdade fundamental?</p><p>Validação ES|QL</p><p>O código ES|QL gerado está sintaticamente correto? É funcionalmente idêntico ao ES|QL original?</p><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><p>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>.</p></li><li><p>Prevemos que <strong>melhoraremos</strong> <strong>a precisão da invocação de nossa ferramenta</strong> em <strong>25%</strong>.</p></li><li><p>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>.</p></li><li><p>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>.</p></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><p>10 novos cenários de ataque</p><p>8 episódios de Oh My Malware (ohmymalware.com)</p><p>4 cenários de múltiplos ataques (criados pela combinação de ataques nas duas primeiras categorias)</p><p>3 cenários benignos</p><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><p>14 Índices que utilizam conjuntos de dados de código aberto para simular múltiplas fontes em KB.</p><p>5 tipos de consulta (analítica, recuperação de texto, híbrida…)</p><p>7 tipos de intenção de consulta (procedimental, factual - classificação, investigativa; …)</p><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><p>Avaliação baseada em regras (por exemplo, (Use Python/TypeScript para verificar se o arquivo .json é válido)</p></li><li><p>LLM como juiz (consultar um LLM separado para verificar se uma resposta é factualmente consistente com um documento original)</p></li><li><p>Revisão com intervenção humana para verificações de qualidade e nuances.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17ec63af0850d8dd/6a17f054505ac3e508ad8c1e/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" alt="Este é um exemplo de resultado de avaliação gerado por nossa estrutura interna. Apresenta diversas métricas de um experimento realizado em diferentes conjuntos de dados." /><h3>Etapa 6: Analisar resultados + iterar</h3><p>Agora que temos as métricas, vamos analisar os resultados. <u><em>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</em></u>; 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62a466f3a0da114a/6a17f056faa91342c393c96c/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" alt="" /><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>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Susan Chang,Abhimanyu Anand]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte578b636637be6b1/6a17f057e8fbcebe9e3a1a36/ef3922076713872163e1aab47735361513b2c9ee-2400x1352.heif" length="0" type="image/*"/>
    <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
  </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>
    <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">"Seu primeiro Elastic Agent"</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><p><strong>O endpoint do cartão do agente (</strong><strong><code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code></strong><strong>) - </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.</p></li><li><p><strong>O ponto final do protocolo A2A (</strong><strong><code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code></strong><strong>)</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>.</p></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><p>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>.</p></li><li><p>Cabeçalho de autenticação: Usaremos uma chave de API padrão para autenticação.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6381135e3fb297df/6a17ef4bec0f898b0c5a66ea/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" alt="Configuração do Cartão de Agente A2A e do Inspetor de Agentes" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f54f0bb15216d8e/6a17ef4d6df73107d90a0fdb/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" alt="Visualizando um agente personalizado em uma lista do Google Agentspace" /><p>Imagine um usuário do Gemini Enterprise perguntando:</p><p><em>"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>"</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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte130c332ee0648a6/6a17ef4fe9ea874426a9c6bb/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" alt="Um usuário envia uma consulta ao Agentspace e o que acontece com essa consulta nos bastidores?" /><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><p>Exponha seu agente por meio do cartão de agente A2A e dos endpoints do protocolo.</p></li><li><p>Testando a conexão com o A2A Inspector.</p></li><li><p>Integrar seu agente em tempo real a um serviço externo como o Gemini Enterprise do Google.</p></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>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Valerio Arvizzigno,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63d7675adc5bc211/6a17ef51ddf97d38e8910bdf/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png" length="0" type="image/png"/>
    <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
  </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>
    <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: "Quais são as últimas notícias sobre a ACME Corp e como isso afeta os investimentos do meu cliente?" 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: "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? 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><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 "esta semana" 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 "habilidade" 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte73e11c1d87593fa/6a17f2134202294dae29f6f2/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" alt="Como criar uma ferramenta no Kibana." /><p>1. Navegue até <strong>Agentes</strong></p><ul><li><p>Clique em<strong> Ferramentas </strong>ou <strong>Gerenciar Ferramentas</strong> e clique no botão <strong>Nova ferramenta</strong> .</p></li></ul><p>2. Preencha o formulário com os seguintes dados:</p><ul><li><p><strong>ID da ferramenta:</strong> <code>find_client_exposure_to_negative_news</code></p></li></ul><p>             eu. Este é o ID exclusivo da ferramenta.</p><ul><li><p><strong>Descrição:</strong> "Identifica a exposição da carteira de clientes a notícias negativas." 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."</p></li></ul><p>             eu. É isso que o LLM lê para decidir se essa ferramenta é a adequada para o trabalho.</p><ul><li><p><strong>Rótulos</strong>: <code>retrieval</code> e <code>risk-analysis</code></p></li></ul><p>         Etiquetas são usadas para ajudar a agrupar várias ferramentas.</p><ul><li><p><strong>Configuração:</strong> Cole a consulta ES|QL completa da Etapa 1.</p></li></ul><p>            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><p><code>time_duration</code>O período de tempo para pesquisar notícias negativas. O formato é "X horas", com o valor padrão de 8760 horas.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7afbb0589c1828ad/6a17f2146864a44e7cb688a9/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" alt="Configurar sua ferramenta, incluindo sua lógica e quaisquer parâmetros necessários, usando uma consulta ESQL. " /><p>4. Teste!</p><ul><li><p>Clique em Salvar e testar.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd09afbef6e21a93/6a17f2162f4a5c73b1fa89fd/57e768b88327821e70bd616744822f98fa367362-732x136.png" alt="O mesmo botão de teste no Kibana." /><ul><li><p>Você verá um novo menu suspenso onde poderá testar a consulta para garantir que ela esteja funcionando conforme o esperado.</p></li></ul><p>             eu. Em <code>time_duration</code> insira o intervalo desejado; aqui, estamos usando “8760 horas”.</p><ul><li><p>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.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bdc3f093363f2a/6a17f217be60861c9c00488a/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" alt="Resposta JSON que aparece após clicar em enviar." /><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>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) &gt;= 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"
  ]
}<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>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.**<p>Vamos analisar por que essa estratégia é tão eficaz:</p><ul><li><p><strong>Define uma persona sofisticada: </strong>a primeira frase estabelece imediatamente o agente como um "Assistente de Inteligência de Dados especializado", definindo um tom profissional e competente.</p></li><li><p><strong>Isso fornece uma estrutura de raciocínio: </strong>ao dizer ao agente para "Compreender, Planejar, Executar e Sintetizar", estamos lhe dando um procedimento operacional padrão. Isso melhora sua capacidade de lidar com questões complexas e de várias etapas.</p></li><li><p><strong>Isso promove o diálogo interativo: </strong>a instrução para "fazer perguntas esclarecedoras" torna o agente mais robusto. Isso minimizará suposições incorretas sobre solicitações ambíguas, levando a respostas mais precisas.</p></li></ul><h3>O Caminho da Interface do Usuário</h3><p>1. Navegue até <strong>Agentes.</strong></p><ul><li><p>Clique em<strong> Ferramentas </strong>ou <strong>Gerenciar Ferramentas</strong> e clique no botão <strong>Nova ferramenta</strong> .</p></li></ul><p>2. Preencha os dados básicos:</p><ul><li><p><strong>ID do agente:</strong> <code>financial_assistant</code>.</p></li><li><p><strong>Instruções: </strong>Copie o enunciado acima.</p></li><li><p><strong>Rótulos</strong>: <code>Finance</code>.</p></li><li><p><strong>Nome de exibição:</strong> <code>Financial Assistant</code>.</p></li><li><p><strong>Descrição da exibição: </strong><code>An assistant for analyzing and understanding your financial data</code>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ac12cbd2b689dee/6a17f219dbb4ff262bfb57ef/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" alt="Criando um assistente financeiro - preenchendo o campo de identificação do agente." /><p>3. De volta ao topo, clique em <strong>Ferramentas</strong>.</p><ul><li><p>Marque a caixa ao lado da nossa ferramenta <code>find_client_exposure_to_negative_news</code> .</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd23556e556a76c5/6a17f21baf47b63a9fcde0a0/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" alt="" /><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>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"
            ]
          }
        ]
      }
    }<h2>Passo 4: A Recompensa — Ter uma Conversa</h2><p>Temos nossa lógica de negócios encapsulada em uma ferramenta e um "cérebro" 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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8826539b16e46f4/6a17f21d505ac35924ad8c5c/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" alt="Ter uma conversa com o Elastic Agent Builder após criar um assistente financeiro." /><h3>O Caminho da Interface do Usuário</h3><ol><li><p>Navegue até <strong>Agentes </strong>no Kibana.</p></li><li><p>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> .</p></li><li><p>Faça uma pergunta que permita ao agente usar nossa ferramenta especializada:</p><ol><li><p><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></p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta1e163fd7c4416bd/6a17f21f6864a4e35bb688ad/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" alt="Uma resposta criada pelo Elastic Agent Builder como assistente financeiro para: clientes com maior risco de sofrer com notícias negativas." /><h3>O que acabou de acontecer? O Raciocínio do Agente</h3><p>O agente não apenas "sabia" 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><p><strong>Intenção identificada:</strong> Correspondeu a palavras-chave da sua pergunta, como "risco" e "notícias negativas", à descrição da ferramenta <code>find_client_exposure_to_negative_news</code> .</p></li><li><p><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.</p></li><li><p><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.</p></li><li><p><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.</p></li></ul><p>E não precisamos apenas supor, se ampliarmos nosso pensamento e observarmos mais detalhes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f6075be8495418/6a17f221af47b65eadcde0a4/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" alt="Os 50 documentos que a assistente financeira encontrou em clientes com maior exposição a notícias negativas." /><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>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}<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><p><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.</p></li><li><p><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.</p></li><li><p><strong><code>/api/agent_builder/converse</code></strong>: O ponto de acesso para interagir com seus agentes, iniciar conversas e obter respostas.</p></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>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe5e78eeb775d715/6a17f2230b0bed719ddd369a/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
  </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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779dae5df010328/6a17e15eabe0f24f18dfe931/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774ffd7df65bd01d/6a17e15f25daabd5cc08a17f/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" alt="" /><ol><li><p>Interprete sua contribuição e objetivo.</p></li><li><p>Selecione a ferramenta e os argumentos corretos para a execução.</p></li><li><p>Analise a resposta da ferramenta.</p></li><li><p>Decida se deseja retornar um resultado ou continuar com outras invocações da ferramenta.</p></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>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<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>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<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 "configuração" contém todos os detalhes importantes, como instruções ou ferramentas disponíveis:</p>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": 
...
   }
}<p>Uma vez criado, o agente pode ser consultado diretamente:</p>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}<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>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"
      }
    }
  ...
  }
...
}<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>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<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>{
  "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...."
  }
}<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 "confiança zero".</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>
    <link>https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[Na Elastic]]></category>
    <dc:creator><![CDATA[Anish Mathur,Dana Juratoni]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16a3d8736bf086e0/6a17e1616864a45410b686c7/71876470119e02a45bcbfcbf27a3e110328bbd14-1020x654.png" length="0" type="image/png"/>
    <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#background-&amp;-application-workflow">Contexto e fluxo de trabalho do aplicativo</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#prepare-data">Preparar dados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#llm-configuration">Configuração LLM</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/ai-powered-dashboards#application-functions">Funções do aplicativo</a></p></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><p>Gemini 2.5 pro</p></li><li><p>GPT o3 / o4-mini-high / 4.1</p></li><li><p>Soneto 4 de Claude</p></li><li><p>Grok 3</p></li><li><p>Deepseek (Deepthink R1)</p></li></ul><p>E para as sugestões, começamos com algo tão simples quanto:</p>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<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9ea005966a783057/6a1707d266c4f90e4ef8bf88/2b599443b5613c9f0fc3235581614add5b4b3900-891x98.png" alt="" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e5632d6d95b998c/6a1707d3a6c2b9441de79661/d87ccfc033bc00ee8188c5cae18043fbca22784c-741x233.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f7738a4c7ddd0cd/6a1707d52b835f0a25f4b166/52c587cf0cf3517fdd4ee7ab95581dd4f2bce030-725x668.png" alt="" /><p></p><p><em>Para simplificar, omitiremos parte do código, mas você pode encontrar o código funcional da aplicação completa neste </em><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"><em><strong>notebook</strong></em></a><em>.</em></p><h2>Pré-requisitos</h2><p>Antes de começar o desenvolvimento, você precisará do seguinte:</p><ol><li><p>Python 3.8 ou superior</p></li><li><p>Um ambiente Python <a href="https://docs.python.org/3/library/venv.html">Venv</a></p></li><li><p>Uma instância do Elasticsearch em execução, juntamente com seu endpoint e chave de API.</p></li><li><p>Uma chave de API da OpenAI armazenada na variável de ambiente com o nome OPENAI_API_KEY:</p></li></ol>export OPENAI_API_KEY="your-openai-api-key"<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>{
    "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
}<p>Agora, vamos obter os mapeamentos do índice que acabamos de carregar, <code>kibana_sample_data_logs</code>:</p>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"]<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>pip install elasticsearch pydantic langchain langchain-openai -q<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>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]<p>Para a entrada de imagem, enviaremos um painel que acabei de desenhar:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7870f6421986d11d/6a1707d78b73cb3408189fa3/36441d7b5dc1f3ff2ac2a30710208d57ad41c716-1600x898.jpg" alt="" /><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>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)}")<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><p>Seção</p><p>Razão</p><p>Você é especialista em analisar dashboards do Kibana a partir de imagens para a versão 9.0.0 do Kibana.</p><p>Ao reforçar isso no Elasticsearch e na versão do Elasticsearch, reduzimos a probabilidade de o LLM gerar parâmetros antigos/inválidos.</p><p>Você receberá uma imagem do painel de controle e um mapeamento do índice do Elasticsearch.</p><p>Explicamos que a imagem se refere a painéis de controle para evitar quaisquer interpretações errôneas por parte do LLM.</p><p>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}</p><p>É 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.</p><p>Inclua apenas os campos relevantes para cada visualização, com base no que está visível na imagem.</p><p>Precisávamos adicionar esse reforço porque, às vezes, o programa tenta adicionar campos que não são relevantes para a imagem.</p><p>Isso retornará um objeto com uma matriz de visualizações para exibir:</p>"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"")"
]<h2>Processando a resposta do LLM</h2><p>NósCriamos 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 "Obter um painel"</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>}
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc55d69d84a08e668/6a1707d8a2929903acd00fb8/ec7e1ac0cd8b470df13e60940162b56778acb386-315x234.png" alt="" /><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>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)<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># 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<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:
</p>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)}")<p>Para executar o script e gerar o painel de controle, execute o seguinte comando no console:</p>python &lt;file_name&gt;.py<p>O resultado final será semelhante a este:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ceffed004153a4f/6a1707d9a929cf9147ae0901/e909afbf0e47d9a6e0f7bd07dfb2efcfa5cf06ac-921x715.png" alt="" /><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>
    <link>https://www.elastic.co/search-labs/blog/ai-powered-dashboards</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-powered-dashboards</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo,Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41727cbee6155a68/6a1707dbb0367dd2fd72bc86/eb60ceb2fbc3941745b21ae3357cbb6ea8fab18c-1443x811.png" length="0" type="image/png"/>
    <pubDate>Wed, 16 Jul 2025 00:00:00 GMT</pubDate>
  </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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63ea3e7a09306fbf/6a17f1d97f6f150e22c09c50/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" alt="" /><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í.
</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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f09493f217047/6a17f1db1d1b83178d93e546/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" alt="" /><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><p><strong>Estruturando o projeto:</strong></p><ol><li><p><strong>Backend (Mastra):</strong> Use o comando `npx create mastra@latest` para criar a estrutura do backend e definir a lógica do agente.</p></li><li><p><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.</p></li></ol></li><li><p><strong>Configurando variáveis de ambiente</strong></p><ol><li><p>Instale o dotenv para gerenciar variáveis de ambiente.</p></li><li><p>Crie um arquivo .env arquive e forneça as variáveis necessárias.</p></li></ol></li><li><p><strong>Configurando o Elasticsearch</strong></p><ol><li><p>Crie um cluster Elasticsearch (localmente ou na nuvem).</p></li><li><p>Instale o cliente oficial do Elasticsearch.</p></li><li><p>Garanta que as variáveis de ambiente estejam acessíveis.</p></li><li><p>Estabelecer conexão com o cliente.</p></li></ol></li><li><p><strong>Ingestão em massa de dados da NBA no Elasticsearch</strong></p><ol><li><p>Crie um índice com os mapeamentos apropriados para habilitar agregações.</p></li><li><p>Importar em massa estatísticas de jogo de jogadores de um arquivo CSV para um índice do Elasticsearch.</p></li></ol></li><li><p><strong>Definir agregações do Elasticsearch</strong></p><ol><li><p>Consulta para calcular as médias históricas contra um adversário específico.</p></li><li><p>Consulta para calcular as médias da temporada contra um adversário específico.</p></li></ol></li><li><p><strong>Arquivo utilitário de comparação de jogadores</strong></p><ol><li><p>Consolida funções auxiliares e agregações do Elasticsearch.</p></li></ol></li><li><p><strong>Construindo o agente</strong></p><ol><li><p>Adicione a definição do agente e o prompt do sistema.</p></li><li><p>Instale o Zod e defina as ferramentas.</p></li><li><p>Adicionar configuração de middleware para lidar com CORS.</p></li></ol></li><li><p><strong>Integrando o frontend</strong></p><ol><li><p>Utilizando o useChat do AI-SDK para interagir com o agente.</p></li><li><p>Crie a interface do usuário para manter conversas formatadas adequadamente.</p></li></ol></li><li><p><strong>Executando o aplicativo</strong></p><ol><li><p>Inicie tanto o backend (servidor Mastra) quanto o frontend (aplicativo React).</p></li><li><p>Exemplos de consultas e uso.</p></li></ol></li><li><p><strong>O que vem a seguir: tornar o agente mais inteligente.</strong></p><ol><li><p>Adicionando recursos de busca semântica para possibilitar recomendações mais relevantes.</p></li><li><p>Habilite consultas dinâmicas movendo a lógica de busca para o servidor Elasticsearch MCP (Model Context Protocol).</p></li></ol></li></ol><h3><strong>Pré-requisitos</strong></h3><ul><li><p><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+).</p></li><li><p><strong>Cluster Elasticsearch:</strong> Um cluster Elasticsearch ativo, seja localmente ou na nuvem.</p></li><li><p><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>.</p></li></ul><p></p><h3><strong>Estrutura do projeto</strong></h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt749baa120552e4ab/6a17f1dd1d1b83bfe993e54a/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" alt="" /><h4><strong>Etapa 1: Estruturando o projeto</strong></h4><ol><li><p>Primeiro, crie o diretório nba-ai-assistant-js e navegue até ele usando: </p></li></ol>mkdir nba-ai-assistant-js &amp;&amp; cd nba-ai-assistant-js<p><strong>Backend:</strong></p><ol><li><p>Utilize a ferramenta de criação do Mastra com o comando: </p></li></ol>npx create-mastra@latest<p>2. Você deverá receber algumas mensagens no seu terminal. Para a primeira, vamos nomear o backend do projeto:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65abf68fe588e968/6a17f1de63baff2814741d5b/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" alt="" /><p>3. Em seguida, manteremos a estrutura padrão para armazenar os arquivos Mastra, então insira <code>src/</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bd829fcf0ae6b9/6a17f1e04b055dd30e432302/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" alt="" /><p>4. Em seguida, escolheremos a OpenAI como nosso provedor padrão de LLM.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd167cc77a40b9a8/6a17f1e11480099e29b48863/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" alt="" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12654151ed495370/6a17f1e22f4a5c0f84fa89f9/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" alt="" /><p><strong>Front-end:</strong></p><ol><li><p>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></p></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><p>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>:</p></li></ol>cd backend
npm install dotenv --save<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># 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
<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><p><strong>Opção A: Usar o Elasticsearch Cloud</strong></p><ul><li><p>Inscreva-se no <a href="https://cloud.elastic.co/registration">Elastic Cloud.</a></p></li><li><p>Criar uma nova implantação</p></li><li><p>Obtenha o URL do seu endpoint e a chave da API (codificada).</p></li></ul></li><li><p><strong>Opção B: Executar o Elasticsearch localmente</strong></p><ul><li><p>Instale e execute o Elasticsearch localmente.</p></li><li><p>Use http://localhost:9200 como seu endpoint.</p></li><li><p>Gere uma chave de API</p></li></ul></li></ul><p></p><p><strong>Instalando o cliente Elasticsearch no servidor:</strong></p><ol><li><p>Primeiro, instale o cliente oficial do Elasticsearch no diretório do seu backend:</p></li></ol>npm install @elastic/elasticsearch<p>2. Em seguida, crie um diretório chamado lib para armazenar funções reutilizáveis e navegue até ele:</p>mkdir lib &amp;&amp; cd lib<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>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 });<p>5. Agora, inicialize o cliente Elasticsearch usando suas variáveis de ambiente e verifique a conexão:</p>//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();
<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><p><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.)</p></li><li><p><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.</p></li></ul><p></p><p><strong>Implementação:</strong></p><ol><li><p>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>.</p></li><li><p>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.</p></li></ol>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');<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: 'strict'</code> para que o Elasticsearch não mapeie dinamicamente campos não reconhecidos. 
</p>// 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;
    }
}
<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>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`);
<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>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) =&gt; {
                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);
    }
}
<p>6. Execute a função <code>main()</code> abaixo para executar sequencialmente as funções <code>createIndex()</code> e <code>bulkIngestCsv()</code> .</p>// 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();
<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>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.' };
    }
}
<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.
</p>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
<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.
</p>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];
}
<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><p><strong>Nome:</strong> Dê ao seu agente um nome que será usado como referência quando ele for chamado na interface.</p></li><li><p><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.</p></li><li><p><strong>Modelo:</strong> Qual modelo de aprendizagem de linguagem (LLM) usar (o Mastra suporta modelos OpenAI, antrópicos, locais, etc.).</p></li><li><p><strong>Ferramentas:</strong> Uma lista de funções de ferramentas que o agente pode chamar.</p></li><li><p><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.</p></li></ul><p></p>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 },
});
<p><strong>
Ferramentas de definição</strong></p><ol><li><p>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> .</p></li><li><p>Instale o Zod usando o comando:</p></li></ol>npm install zod<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><p><code>id</code>Esta é uma descrição em linguagem natural para ajudar o agente a entender o que a ferramenta faz.</p></li><li><p><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.</p></li><li><p><code>description</code>Esta é uma descrição em linguagem natural para ajudar o agente a entender quando ligar e usar a ferramenta.</p></li><li><p><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.</p></li></ul>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 } }) =&gt; {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})<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><p><code>origin: ['http://localhost:5173']</code></p><ul><li><p>Permite solicitações somente deste endereço (endereço padrão do Vite)</p></li></ul></li><li><p><code>allowMethods: ["GET", "POST"]</code></p><ul><li><p>Métodos HTTP permitidos. Na maioria das vezes, será utilizado o método POST.</p></li></ul></li><li><p><code>allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],</code></p><ul><li><p>Essas configurações definem quais cabeçalhos personalizados podem ser usados nas solicitações.</p></li></ul></li></ul><p></p>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
<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>npm install @ai-sdk/react<p>2. Ainda no mesmo diretório, instale o React Markdown para que possamos formatar corretamente a resposta gerada pelo agente.</p>npm install react-markdown<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><p><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.</p></li><li><p><code>onToolCall</code>Este comando é executado sempre que o agente chama uma ferramenta; estamos usando-o para rastrear quais ferramentas nosso agente está chamando.</p></li><li><p><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.</p></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>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 }) =&gt; {
            setToolsCalled((prev) =&gt; [...prev, toolCall.toolName]);
        },

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

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

    return (
        &lt;div&gt;
            &lt;div className="agent-info"&gt;
                &lt;h4 className="stats-title"&gt;What's My Agent Doing?&lt;/h4&gt;

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

                    &lt;div className="usage-stats"&gt;
                        &lt;p&gt;Prompt Token Usage: {promptTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Completion Token Usage: {completionTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Total Token Usage: {totalTokenUsage}&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;

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

            &lt;form onSubmit={handleSubmit}&gt;
                &lt;input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                /&gt;
                &lt;button type="submit" disabled={status === 'streaming'}&gt;
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    );
}<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><p>Em uma janela de terminal, partindo do diretório raiz, navegue até o diretório de backend e inicie o servidor Mastra:</p></li></ol>cd backend

npm run dev<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>cd frontend

npm run dev<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><p>"Compare LeBron James e Stephen Curry"</p></li><li><p>"Quem devo escolher entre Jayson Tatum e Luka Doncic?"</p></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><p><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.</p></li><li><p><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).</p></li><li><p><strong>O framework Mastra </strong>e como ele simplifica a criação desses agentes para desenvolvedores no ecossistema JavaScript.</p></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>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[Javascript]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ffd561836a4cb20/6a17f1e47b54f978588b39e4/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>Índice e modelo de pesquisa do Elasticsearch</p></li><li><p>Azure OpenAI</p></li><li><p>API do Azure Maps</p></li><li><p><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb">Caderno Jupyter de espaços de código</a></p></li><li><p>Kernel Semântico</p></li><li><p>Aplicativo C# com frontend Blazor</p></li></ul><h2>Fluxo de trabalho de consulta inteligente</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0461b58012efd772/6a17f73fa292997d52d02e19/0c4a7c835e06c514f158c00ab1055a7ba719a35f-1600x765.png" alt="Fluxo de trabalho de consulta inteligente" /><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><p><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.</p></li><li><p><strong>Ferramentas LLM</strong> - Cada ferramenta LLM é um programa em C# que criamos para este post. Existem três ferramentas:</p><ul><li><p><em>Ferramenta de extração de parâmetros</em>: extrai atributos-chave como quartos, banheiros, recursos e preço da consulta.</p></li><li><p><em>Ferramenta GeoCode</em>: converte nomes de locais em latitude/longitude para filtragem espacial.</p></li><li><p><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.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7617ae80295a3e2b/6a17f74196142a35deeb1cb0/2880afee184cd9270c0eb4310e51418e2339784d-936x452.png" alt="Diagrama da arquitetura do sistema de um aplicativo Azure LLM Functions." /><h2>Pré-requisitos</h2><p>Você só precisará do seu navegador, porque usaremos o GitHub Codespaces para clonar o aplicativo de exemplo, configurá-lo e executá-lo. Para a parte elástica da solução, usaremos o Elastic Cloud para criar um projeto Elasticsearch Serverless. Usaremos <a href="https://portal.azure.com/">o Portal do Azure</a> para trabalhar com recursos do Azure.</p><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ed9c67e2d79c41/6a17f7436df73146a20a10bc/b89cbec491659b6c8a0bb9551ed2629f7a37f9fd-1600x427.png" alt="Clonando um repositório de aplicativo de exemplo no Codespaces." /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdfcc992a6cb7a7d7/6a17f7450b0bed67c1dd3750/43ea377554527af9578400f16cd2342bf8fff3a2-1600x1049.png" alt="No menu suspenso do repositório, clique em Criar Codespace." /><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 <em><strong>.env</strong></em> e você irá criá-lo agora.</p><ol><li><p>No GitHub Codespaces, clique no botão <strong>Novo arquivo </strong>e adicione um arquivo chamado <em><strong>.env</strong></em></p></li><li><p>Adicione o seguinte conteúdo ao <em><strong>.env</strong></em> recém-criado arquivo</p></li></ol>ELASTIC_URL=
ELASTIC_API_KEY=<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a7dcb2fd2bb24f5/6a17f7462f4a5c21abfa8aa9/84d4f327948858ba61db0001dd8cf780d42fe0a7-1600x875.gif" alt="Um notebook Jupyter em Python permite acessar e interagir com o Elastic Cloud, utilizando os valores de configuração armazenados em um arquivo de configuração. " /><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><p>Acesse <a href="http://cloud.elastic.co">cloud.elastic.co</a> e clique em <strong>Criar novo projeto sem servidor</strong></p></li><li><p>Clique em <strong>Avançar </strong>para a solução <strong>Elasticsearch</strong></p></li><li><p>Selecione <strong>Otimizado para Vetores</strong></p></li><li><p>Defina o <strong>Provedor de Nuvem</strong> como <strong>Azure</strong></p></li><li><p>Clique em <strong>Criar projeto sem servidor</strong></p></li><li><p>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></p></li><li><p>Clique no botão <strong>Copiar </strong>para copiar o <strong>Endpoint do Elasticsearch</strong> dos <strong>Detalhes da Conexão</strong></p></li><li><p>Atualizar <em><strong>.env</strong></em> arquivo para definir o <strong>ELASTIC_URL</strong> como o <strong>Elasticsearch Endpoint</strong>copiado</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95b7fefa05173822/6a17f748ec0f89f05f5a67c6/77a35e55446d396066b68cfd132d1543a07b81cc-1600x875.gif" alt="Como criar um novo projeto Serverless no Elasticsearch." /><h2>Criar uma chave de API elástica</h2><ol><li><p>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></p></li><li><p>Digite um <strong>nome</strong>de chave</p></li><li><p>Clique em<strong> Criar chave de API</strong></p></li><li><p>Clique no botão Copiar para copiar o valor da chave da API</p></li><li><p>De volta ao <strong>Codespaces,</strong> onde temos o <em><strong>.env</strong></em>arquivo aberto para edição, cole o valor copiado para definir a<strong> ELASTIC_API_KEY</strong></p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf6b1c85d267e6ad0/6a17f74a4b055d118143239a/20168cba493d8e2c0d9ae7704eb0ae707df58e4c-1600x875.gif" alt="Como criar uma chave de API do Elasticsearch." /><h2>Abra o Codespaces Notebook e instale as dependências da biblioteca</h2><p>No explorador de arquivos, selecione o arquivo <em><strong>VectorDBSetup.ipynb</strong></em> 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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L40-L52"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt970f4fa30c6c9302/6a17f74c505ac30ee6ad8ceb/2272f70615dfb9dcbeb91f39b6dd5076213e24a5-1600x875.gif" alt="Instalando dependências de biblioteca no Notebook do Codespaces." /><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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L58-L104"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6d74b41258420d5a/6a17f74e6317301f2f585c16/aa9f9198ff452ac0c4ce33b00f253731dbee22c5-1600x875.gif" alt="Definindo importações e carregando variáveis de ambiente usando o Codespaces Notebook." /><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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L112-L157"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87581300d4d0b66e/6a17f750e9ea875a81a9c795/97c1afab3e64027ee5ae77f377d56ba406ae1765-1600x875.gif" alt="Criando um endpoint de inferência do Elastic ML usando o Codespaces Notebook." /><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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L165-L224"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta2d2d5a10c84a1b7/6a17f7527f6f15775cc09cd6/23a66283ee41239e24fb8455c3cd95641982ca6b-1600x875.gif" alt="Criando um índice Elasticsearch usando o Codespaces Notebook." /><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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L232-L384"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt194d6557d096ac25/6a17f7545772628d901bcda0/4c001a3e4d1cca4cfb5c043fea92c7ccaf9cb64a-1600x875.gif" alt="Criando um modelo de pesquisa Elasticsearch usando o Codespaces Notebook." /><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 </a><a href="https://github.com/jwilliams-elastic/msbuild-intelligent-query-demo/blob/main/VectorDBSetup.ipynb?short_path=17c25d8#L392-L450"><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaf1b5ad59c75716d/6a17f7566864a4528fb6894b/e9698c798541ccfc08143a939846597028e3c566-1600x875.gif" alt="Ingestão de dados no índice do Elasticsearch usando o Codespaces Notebook." /><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>{
 "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": "*"
}
<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt28afc316da403ec9/6a17f758faa913584d93ca28/00dad25bacdea2adcbd1e6eca7658867a49b0d8c-1600x875.gif" alt="Criando o arquivo appsetting.json para configurar o aplicativo C#." /><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><p>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></p></li><li><p>No formulário de criação, escolha um <strong>Grupo de Recursos</strong>.</p></li><li><p>Digite um <strong>nome</strong></p></li><li><p>Escolha um <strong>nível de preço</strong></p></li><li><p>Clique <strong>em Avançar</strong></p></li><li><p>Na guia <strong>Rede</strong> , clique em<strong> Avançar</strong></p></li><li><p>Na aba <strong>Tags</strong> , clique em <strong>Avançar</strong></p></li><li><p>Na guia <strong>Revisar e enviar</strong> , clique em <strong>Criar</strong></p></li><li><p>Após a conclusão da criação, clique em <strong>Ir para o recurso</strong></p></li><li><p>Selecione <strong>Chaves e Ponto de Extremidade</strong> no menu de navegação à esquerda</p></li><li><p>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.</p></li><li><p>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.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd01a5e82d9003d02/6a17f75a148009be65b48904/6d49197302d110410dca0a53b6ae90237cf2dfd6-1600x875.gif" alt="Criando um serviço Azure OpenAI." /><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><p>Acesse o <a href="https://ai.azure.com/resource/playground">Azure AI Foundry</a> e clique em <strong>Criar uma implantação</strong></p></li><li><p>Pesquise por <em>gpt-4o</em> e selecione-o nos resultados</p></li><li><p>Clique em <strong>Confirmar</strong> para selecioná-lo</p></li><li><p>Clique em <strong>Implantar</strong> para implantar o modelo</p></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 <em><strong>do gpt-4o</strong></em> está listada com o <strong>Estado</strong> <strong>Succeeded</strong>.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte82341dd8a4982b0/6a17f75c4b055d9e0943239e/1b817ab67c05634e9c72777593b4d1a2c6c28191-1600x875.gif" alt="Adicionando uma implantação de modelo gpt-4o ao serviço Azure Open AI." /><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><p>Clique em <strong>Criar</strong> nas <a href="https://portal.azure.com/#browse/Microsoft.Maps%2Faccounts">contas do Azure Maps</a></p></li><li><p>Selecione um <strong>grupo de recursos</strong></p></li><li><p>Digite um <strong>nome</strong></p></li><li><p>Concordo com a declaração de licença e privacidade</p></li><li><p>Clique em <strong>Revisar e criar</strong></p></li><li><p>Clique em <strong>Criar</strong></p></li><li><p>Após a criação da conta, clique em <strong>Ir para o recurso</strong></p></li><li><p>Clique em <strong>Autenticação</strong> no menu de navegação à esquerda</p></li><li><p>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</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42a4ab4bb7a96d24/6a17f75edbb4ff4f91fb5892/90fadd48e366682e2bad91e32988f93c6354e126-1600x875.gif" alt="Criar uma conta do Azure Maps." /><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.
</p>cd HomeFinderApp<p>3. Use o seguinte comando <em>dotnet</em> para executar o aplicativo.</p>dotnet run<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41adf6631ba91be1/6a17f760505ac30986ad8cf1/821fe7b9446de5ed646d938cc9484a7ddad21030-1600x875.gif" alt="Testando o aplicativo de exemplo." /><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>
    <link>https://www.elastic.co/search-labs/blog/azure-llm-functions-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/azure-llm-functions-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jonathan Simon,James Williams]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93dd59caccfd7fc8/6a17f7614202292a7129f799/1431b90c7e00de06574c1e33c44a2e89296c824e-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate>
  </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>
    <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5ff0e141b5dfda29/6a17e7ffe8fbcee5263a1946/5eba1e59514eb58a5220bb92bb49e6328ee83cd7-674x466.png" alt="Arquitetura do Protocolo de Contexto do Modelo (MCP)" /><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><p>Para habilitar o MCP remoto com a adição do Streamable HTTP</p></li><li><p>Para permitir modelos de interação de agentes mais ricos com a adição de Elicitação e Esquemas de Saída de Ferramentas</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d53e7bb091b4f43/6a17e80163baff80dc741c56/2ea159116fe5e03ff800f077adf16d6ca9f1c1d1-1594x1280.png" alt="Autenticação MCP para agentes" /><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><p><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></p></li><li><p><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.</p></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><p>Ameaça</p><p>Descrição</p><p>Remediação e melhores práticas</p><p>Injeção imediata e envenenamento por ferramentas</p><p>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.</p><p>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.</p><p>Ataques de amostragem</p><p>No contexto do MCP, a amostragem abre a porta para o servidor MCP realizar ataques de injeção rápida no LLM.</p><p>Desative a amostragem para servidores não confiáveis e considere adicionar aprovações humanas para solicitações de amostragem.</p><p>Servidores MCP maliciosos</p><p>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.</p><p>Conecte-se somente a servidores MCP de registros confiáveis ou listas internas. Execute servidores de terceiros em contêineres com sandbox.</p><p>Ferramentas de instalação de MCP maliciosas</p><p>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.</p><p>Instale em ambientes sandbox e valide assinaturas de pacotes. Nunca atualize automaticamente a partir de fontes não verificadas.</p><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><p><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices">Melhores práticas oficiais de segurança do MCP</a></p></li><li><p><a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">Top 10 de inscrições para o OWASP LLM</a></p></li><li><p><a href="https://hiddenlayer.com/innovation-hub/">Pesquisa de ameaças HiddenLayer</a></p></li><li><p><a href="https://github.com/invariantlabs-ai/mcp-scan">Varredura MCP</a></p></li><li><p><a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/">Guia de injeção rápida OWASP</a></p></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><p>Oferecendo uma única fonte de verdade para metadados do servidor (o que um servidor faz, como autenticar, instalá-lo e chamá-lo)</p></li><li><p>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.</p></li><li><p>Fornecendo um fluxo de registro de servidor que inclui uma ferramenta CLI e um arquivo server.json que contém os metadados mencionados anteriormente.</p></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><p><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.
</p></li><li><p><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.
</p></li><li><p><strong>Postman - </strong><a href="https://www.postman.com/explore/mcp-generator"><strong>Gerador de servidor MCP</strong></a><strong>: </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.
</p></li><li><p><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.
</p></li><li><p><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.
</p></li><li><p><strong>AWS - </strong><a href="https://github.com/awslabs/mcp"><strong>Servidores MCP da AWS</strong></a><strong>: </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.</p></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><p><code>platform.core.search</code> - Executa pesquisas usando a DSL de consulta completa do Elasticsearch</p></li><li><p><code>platform.core.list_indices</code> - Lista todos os índices disponíveis no Elasticsearch (ajuda os agentes a descobrir quais dados existem)</p></li><li><p><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)</p></li><li><p><code>platform.core.get_document_by_id</code> - Busca um documento específico por ID (para uma recuperação precisa)</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca9e3d0a4e7031c0/6a17e803faa913d8c393c897/c1f6405a374b707e8e6fa36b9e21db5f3c7cd127-1376x864.png" alt="Interface do usuário do Construtor de Agentes" /><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7d8b29b06c08f94/6a17e805033c8d07f06bb1b6/9f39588525ca2643475de557ea54a6bcf5c150f6-1282x616.png" alt="Ferramentas MCP" /><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>
    <link>https://www.elastic.co/search-labs/blog/mcp-current-state</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/mcp-current-state</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2f63f23bbecd2a18/6a17e8066317302039585aa7/02b8c8672ffa129e0ed91a92d6cab612a01d27f2-1200x628.png" length="0" type="image/png"/>
    <pubDate>Thu, 12 Jun 2025 00:00:00 GMT</pubDate>
  </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>
    <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><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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltee1f144eb9e74866/6a17e379dbb4ffdf4bfb5647/328d7c51e1c145e94ea1e73ee9ff91836d3b180e-1600x773.png" alt="Como usar o Spring AI com o Elasticsearch" /><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><p><strong>Modelos de imagem</strong>: geram imagens a partir de instruções de texto.</p></li><li><p><strong>Modelos de transcrição</strong>: pegam fontes de áudio e as convertem em texto.</p></li><li><p><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.</p></li><li><p><strong>Modelos de bate-papo: </strong>estesDeve ser familiar! Você sem dúvida já teve até mesmo uma breve conversa com um deles em algum lugar.</p></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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd2d062ded38cf83/6a17e37adbb4ff69d7fb564b/2ebd68a90ebc73847df6ef7325936d4d06b35c8c-1600x900.jpg" alt="Soluções Spring AI para desafios de IA" /><p>Vamos analisar alguns desses problemas e suas soluções no Spring AI.</p><p></p><p>Problema</p><p>Solução</p><p>Consistência</p><p>Os modelos de bate-papo são de mente aberta e propensos a distrações.</p><p>Você pode fornecer a eles um comando do sistema para controlar sua forma e estrutura geral.</p><p>Memória</p><p>Os modelos de IA não têm memória, portanto não conseguem correlacionar uma mensagem de um determinado usuário com outra.</p><p>Você pode fornecer a eles um sistema de memória para armazenar as partes relevantes da conversa.</p><p>Isolamento</p><p>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.</p><p>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ê.</p><p>Dados privados</p><p>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!</p><p>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.</p><p>Alucinação</p><p>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.</p><p>É necessário usar a avaliação — utilizando um modelo para validar a saída de outro — para confirmar resultados razoáveis.</p><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. <a href="https://modelcontextprotocol.io/introduction"><strong>Protocolo de Contexto do Modelo</strong></a>(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 <a href="https://start.spring.io"><strong>Spring Initializr</strong></a><strong>.</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><p>Super fácil de configurar</p></li><li><p>Código aberto</p></li><li><p>Escalável horizontalmente</p></li><li><p>A maior parte dos dados de formato livre da sua organização provavelmente já reside em um cluster Elasticsearch.</p></li><li><p>Funcionalidade completa de mecanismo de busca</p></li><li><p>Totalmente <a href="https://docs.spring.io/spring-ai/reference/api/vectordbs/elasticsearch.html">integrado ao Spring AI</a>!</p></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> 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
➜  ~ <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><p><code>Elasticsearch Vector Store</code></p></li><li><p><code>Spring Boot Actuator</code></p></li><li><p><code>GraalVM</code></p></li><li><p><code>OpenAI</code></p></li><li><p><code>Web</code></p></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>spring.elasticsearch.uris=http://localhost:9200
spring.elasticsearch.username=elastic
spring.elasticsearch.password=w1GB15uQ<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>spring.ai.vectorstore.elasticsearch.initialize-schema=true<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>spring.ai.openai.api-key=...<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>spring.servlet.multipart.max-file-size=20MB
spring.servlet.multipart.max-request-size=20MB<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>http --form POST http://localhost:8080/rag/ingest path@/Users/jlong/Downloads/test.pdf<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><p>O cliente HTTP carrega o PDF de sua escolha para a aplicação Spring.</p></li><li><p>O Spring AI se encarrega da extração de texto do nosso PDF e divide cada página em blocos de 800 caracteres.</p></li><li><p>A OpenAI gera a representação vetorial para cada segmento.</p></li><li><p>Tanto o texto fragmentado quanto o arquivo incorporado são armazenados no Elasticsearch.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f4a64b634e9cce/6a17e37cb1e113215879f216/9734adb2d7128e61c515d5855dfad6d3a326a4a1-1454x706.png" alt="Fluxo de trabalho completo para extração de texto do Spring AI do PDF, representação vetorial do Open AI e fragmentação de texto do Elasticsearch para criar embeddings." /><p>Por fim, vamos fazer uma consulta:</p>http :8080/rag/query question=="where do you place the reward card after obtaining it?" <p>E obteremos uma resposta relevante:</p>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<p>Que legal! Como tudo isso funciona?</p><ul><li><p>O cliente HTTP envia a pergunta para a aplicação Spring.</p></li><li><p>O Spring AI obtém a representação vetorial da pergunta da OpenAI.</p></li><li><p>Com essa incorporação, ele busca documentos semelhantes nos blocos armazenados do Elasticsearch e recupera os documentos mais similares.</p></li><li><p>A Spring AI envia então a pergunta e o contexto obtido para a OpenAI para gerar uma resposta LLM.</p></li><li><p>Por fim, retorna a resposta gerada e uma referência ao contexto recuperado.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfab41731851104f3/6a17e37e445de90e924d00aa/3799de6e8cb13ce49b9e136cfe593263030231a8-1464x1050.png" alt="Fluxo de trabalho completo do Spring AI e do Open AI para gerar a resposta do LLM a uma pergunta." /><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>@SpringBootApplication
public class DemoApplication {
 	public static void main(String[] args) { 
     		SpringApplication.run(DemoApplication.class, args);
 	}
}<p>Nada para ver ali. Continuando…</p><p>Em seguida, um controlador HTTP básico:</p>@RestController
class RagController {

   private final RagService rag;

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

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

   @GetMapping("/rag/query")
   ResponseEntity&lt;?&gt; query(@RequestParam String question) {
       String response = rag.directRag(question);
       return ResponseEntity.ok().body(response);
   }
}<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>@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&lt;Document&gt; batch = new TokenTextSplitter().apply(pdfReader.read());
       vectorStore.add(batch);
   }

  // TBD
}<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45805a5b2da5e336/6a17e3803e03d70a584f2bda/c85e522f02f8b2da7462cd428dc7e952c9692542-1600x1040.png" alt="Como criar uma consulta na instância do Elasticsearch." /><p>Faça uma consulta como esta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt21d79210fe213b1e/6a17e382e3179163492d5767/00974a176cbce11e70fcab24fb4b3f9c6e205982-1600x1040.png" alt="Emitindo uma consulta no Elasticsearch Console." /><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>String directRag(String question) {
   // Query the vector store for documents related to the question
   List&lt;Document&gt; 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";

}<p>O código é bastante simples, mas vamos dividi-lo em várias etapas:</p><ol><li><p>Use <code>VectorStore</code> para realizar uma pesquisa de similaridade.</p></li><li><p>Dados todos os resultados, obtenha os <code>Document</code>subjacentes do Spring AI e extraia seu texto, concatenando-os em um único resultado.</p></li><li><p>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.</p></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:
</p>&lt;dependency&gt;
   &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
   &lt;artifactId&gt;spring-ai-advisors-vector-store&lt;/artifactId&gt;
&lt;/dependency&gt;<p>Adicione outro método chamado <code>advisedRag(String question)</code> à classe:</p>String advisedRag(String question) {
   return this.ai
           .prompt()
           .user(question)
           .advisors(new QuestionAnswerAdvisor(vectorStore))
           .call()
           .content();
}<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>management.endpoints.web.exposure.include=*<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:
</p>spring.threads.virtual.enabled=true<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>
    <link>https://www.elastic.co/search-labs/blog/spring-ai-elasticsearch-application</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/spring-ai-elasticsearch-application</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Josh Long,Philipp Krenn,Laura Trotta]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt26b868ef618164c6/6a17e3830b0bedd68cdd3515/0771fb5b3d9234697cb868cd7d9d1b840000bf29-1280x720.png" length="0" type="image/png"/>
    <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
  </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>
    <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><p>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</p></li><li><p>Python 3.9+</p></li><li><p>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></p></li></ul><h2>Criando o aplicativo LangGraph</h2><h3>1. Instale o LangGraph CLI</h3>pip install --upgrade "langgraph-cli[inmem]"<h3>2. Crie o aplicativo LangGraph a partir do modelo de agente de recuperação</h3>mkdir lg-agent-demo
cd lg-agent-demo
langgraph new lg-agent-demo <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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd44177037ea46d45/6a17f86b3e9e45265fba1663/6a41a41f95c2477c67810adc7be46d91faf06878-1600x407.png" alt="Modelo de recuperação interativa." /><ul><li><p><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; “</p></li></ul><p>Execute o comando Instalar Certificado do Python para resolver o problema, conforme mostrado abaixo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbbe2d1d3a1af75b1/6a17f86d445de97c9c4d02e7/83ec238136c41738457299fd42c83aff32eb5b97-1407x75.png" alt="Executando o comando de instalação do certificado em Python." /><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>#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 .<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>cp .env.example .env<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># 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<ul><li><p>Exemplo de arquivo <code>.env</code> (usando Elastic Cloud e Cohere)</p></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># 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<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 </em><em><strong>OpenAI</strong></em><em>, </em><em><strong>Claude</strong></em><em>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><em>.</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> cd src/retrieval_graph<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><p><strong>Modelo de incorporação</strong> – converte documentos em representações vetoriais</p></li><li><p><strong>Modelo de consulta</strong> – processa a consulta do usuário em um vetor</p></li><li><p><strong>Modelo de resposta</strong> – gera a resposta final</p></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>).

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>…
 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={<h2>Executando o agente de recuperação com a CLI do LangGraph</h2><h3>1. Inicie o servidor LangGraph</h3>cd lg-agent-demo
langgraph dev<p>Isso iniciará o servidor LangGraph API localmente. Se isso for executado com sucesso, você deverá ver algo como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt46c7a703e715ef66/6a17f86eb1e113272d79f42e/e3c3344b24651067e2d0892d870feca505b3be35-1494x542.png" alt=" Servidor da API LangGraph em execução com sucesso." /><p>URL da interface do usuário do Open Studio.</p><p>Há dois gráficos disponíveis:</p><ul><li><p><strong>Gráfico de recuperação</strong>: Recupera dados do Elasticsearch e responde à consulta usando um LLM (Language-Level Model).</p></li><li><p><strong>Gráfico do indexador</strong>: Indexa documentos no Elasticsearch e gera embeddings usando um LLM.</p></li></ul><h3>2. Configurando o gráfico do indexador</h3><ul><li><p>Abra o gráfico do indexador.</p></li><li><p>Clique em Gerenciar assistentes.</p><ul><li><p>Clique em <strong>'Adicionar novo assistente</strong> ', insira os dados do usuário conforme especificado e, em seguida, feche a janela.</p></li></ul></li></ul>{"user_id": "101"}<h3>3. Indexação de documentos de amostra</h3><ul><li><p>Indexe os seguintes documentos de exemplo, que representam um relatório trimestral hipotético para a organização NoveTech:</p></li></ul>[
  {    "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&amp;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.'"
  }
]<p>Depois que os documentos forem indexados, você verá uma mensagem de exclusão no tópico, conforme mostrado abaixo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt38715eadffcb62f0/6a17f877faa9135f7393ca4c/fd3a1efd64cb54d54ea56ef5055249dd066d5708-1600x854.png" alt="Documentos de fluxo de trabalho RAG do LangGraph e do Elasticsearch indexados." /><h3>4. Executando o grafo de recuperação</h3><ul><li><p>Mude para o gráfico de recuperação.</p></li><li><p>Digite a seguinte consulta de pesquisa:</p></li></ul>What was NovaTech Solutions total revenue in Q1 2025?<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c0d070fca52e512/6a17f879505ac36d37ad8d12/eb4d8ddfe0effd7e1868fba921b8ef13f7baf27a-1600x755.png" alt="Executando o gráfico de recuperação LangGraph e Elasticsearch" /><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><p>Contexto dos documentos recuperados</p></li><li><p>Interações anteriores do usuário</p></li><li><p>Última consulta do usuário</p></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><p>Adicione a função <code>predict_query</code> :</p></li></ul>async def predict_query(
   state: State, *, config: RunnableConfig
) -&gt; 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]}<ul><li><p>Modifique a função <code>respond</code> para retornar o objeto <strong><code>response</code></strong> , em vez da mensagem:</p></li></ul>async def respond(
   state: State, *, config: RunnableConfig
) -&gt; 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]}<ul><li><p>Atualizar estrutura do gráfico para adicionar novo nó e aresta para predict_query:</p></li></ul>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")<h3>2. Atualize o arquivo prompts.py</h3><ul><li><p>Crie um prompt para previsão de guery em <code>prompts.py</code>:</p></li></ul>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}"""<h3>3. Atualize o arquivo configuration.py</h3><ul><li><p>Adicionar <code>predict_next_question_prompt</code>:</p></li></ul>predict_next_question_prompt: str = field(
       default=prompts.PREDICT_NEXT_QUESTION_PROMPT,
       metadata={"description": "The system prompt used for generating responses."},
   )<h3>4. Atualize o arquivo state.py</h3><ul><li><p>Adicione os seguintes atributos:</p></li></ul>response: Annotated[Sequence[AnyMessage], add_messages]
next_question : Annotated[Sequence[AnyMessage], add_messages]<h3>5. Execute novamente o grafo de recuperação.</h3><ul><li><p>Digite a seguinte consulta de pesquisa novamente:</p></li></ul>What was NovaTech Solutions total revenue in Q1 2025?<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://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8d4de9396ca4853/6a17f87be31791dc742d59c7/70e855a2e4edc0ba5a147588df0de30eb081d053-1600x777.png" alt="Executando o gráfico de recuperação com 3 perguntas de usuários usando LangGraph e Elasticsearch." /><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><p><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.</p></li><li><p><strong>Implantação perfeita</strong>: o suporte integrado para APIs e o dimensionamento automático garantem uma implantação tranquila em todos os ambientes.</p></li><li><p><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.</p></li><li><p><strong>Memória persistente</strong>: o sistema retém os estados e o conhecimento dos agentes, melhorando a consistência e a confiabilidade.</p></li><li><p><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.</p></li><li><p><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.</p></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>
    <link>https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Neha Saini]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c9f03d2c1a9cb4c/6a17f87d0b0bed3781dd377c/17b7e7b336f73e232375d1add582ae5f6c52a279-1440x840.png" length="0" type="image/png"/>
    <pubDate>Fri, 25 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Utilizando modelos do Amazon Nova no Elasticsearch]]></title>
    <description><![CDATA[Aprenda a usar os modelos Amazon Nova no Elasticsearch para extrair automaticamente sentimentos, autenticidade, resumos e palavras-chave das avaliações de produtos no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, discutiremos a família de modelos de IA da Amazon, o Amazon Nova, e aprenderemos como usá-lo em conjunto com o Elasticsearch.</p><h2>Sobre a Amazon Nova</h2><p>O Amazon Nova é uma família de modelos de inteligência artificial da Amazon, disponível na plataforma Amazon Bedrock e projetada para oferecer alto desempenho e custo-benefício. Esses modelos operam com entradas de texto, imagem e vídeo, geram saídas textuais e são otimizados para diferentes necessidades de precisão, velocidade e custo.</p><h3>Modelos principais do Amazon Nova</h3><ul><li><p>Amazon Nova Micro: Focado exclusivamente em texto, este é um modelo rápido e econômico, ideal para tradução, raciocínio, preenchimento automático de código e resolução de problemas matemáticos. Sua geração ultrapassa 200 tokens por segundo, tornando-o ideal para aplicações que exigem respostas instantâneas.</p></li><li><p>Amazon Nova Lite: modelo multimodal de baixo custo capaz de processar rapidamente imagens, vídeos e textos. Ele se destaca pela rapidez e precisão, sendo indicado para aplicações interativas e de alto volume onde o custo é um fator relevante.</p></li><li><p>Amazon Nova Pro: A opção mais avançada, que combina alta precisão, velocidade e custo-benefício. Ideal para tarefas complexas como resumo de vídeos, perguntas e respostas, desenvolvimento de software e agentes de IA. Avaliações de especialistas atestam sua excelência na compreensão textual e visual, bem como sua capacidade de seguir instruções e executar fluxos de trabalho automatizados.</p></li></ul><p>Os modelos Amazon Nova são adequados para uma variedade de aplicações, desde a criação de conteúdo e análise de dados até o desenvolvimento de software e a automação de processos com inteligência artificial.</p><p>A seguir, demonstraremos como usar os modelos do Amazon Nova em conjunto com o Elasticsearch para análise automatizada de avaliações de produtos.</p><p>O que faremos:</p><ol><li><p>Crie um endpoint por meio da API de Inferência, integrando o Amazon Bedrock com o Elasticsearch.</p></li><li><p>Crie um pipeline usando o Processador de Inferência, que fará chamadas para o endpoint da API de Inferência.</p></li><li><p>Indexe as avaliações de produtos e gere automaticamente uma análise dessas avaliações usando o pipeline.</p></li><li><p>Analise os resultados da integração.</p></li></ol><h2>Criando um endpoint na API de inferência com Amazon Nova Lite</h2><p>Primeiro, configuramos a API de Inferência para integrar o Amazon Bedrock com o Elasticsearch. Definimos o Amazon Nova Lite, id <strong>amazon.nova-lite-v1:0</strong>, como o modelo a ser utilizado, pois oferece um equilíbrio entre velocidade, precisão e custo.</p><p><strong>Observação:</strong> você precisará de credenciais válidas para usar o Amazon Bedrock. Você pode consultar a documentação para obter as chaves de acesso <a href="https://docs.aws.amazon.com/keyspaces/latest/devguide/create.keypair.html">aqui</a>:</p>PUT _inference/completion/bedrock_completion_amazon_nova-lite
{
   "service": "amazonbedrock",
   "service_settings": {
       "access_key": "#access_key#",
       "secret_key": "#secret_key#",
       "region": "us-east-1",
       "provider": "amazontitan",
       "model": "amazon.nova-lite-v1:0"
   }
}<h2>Criando o pipeline de análise de revisão</h2><p>Agora, criamos um pipeline de processamento que usará o Processador de Inferência para executar uma solicitação de análise de revisão. Este comando enviará os dados da avaliação para o Amazon Nova Lite, que executará as seguintes ações:</p><ul><li><p>Classificação do sentimento (positivo, negativo ou neutro).</p></li><li><p>Resumo da revisão.</p></li><li><p>Geração de palavras-chave.</p></li><li><p>Medição de autenticidade (autêntico | suspeito | genérico).</p></li></ul>PUT /_ingest/pipeline/review_analyzer_ai
{
      "processors": [
      {
        "script": 
            {
            "source": """ctx.prompt = "Analyze the following product review and return a structured JSON. Task: - Summarize the review concisely. - Detect and classify the sentiment as positive, neutral, or negative.- Generate relevant tags (keywords) based on the review content and detected sentiment. - Evaluate the authenticity of the review (authentic, suspicious, or generic). Review: " + ctx.review + " Respond in JSON format with the following fields: \"review_analyze\": {\"sentiment\": \"&lt;positive | neutral | negative&gt;\", \"authenticity\": \"&lt;authentic | suspicious | generic&gt;\",\"summary\": \"&lt;short review summary&gt;\", \"keywords\": [\"&lt;keyword 1&gt;\", \"&lt;keyword 2&gt;\", \"...\"]}}}"
            """
            }
      },
      {
        "inference": {
          "model_id": "bedrock_completion_amazon_nova-lite",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "gsub": {
          "field": "result",
          "pattern": "```json",
          "replacement": ""
        } 
      },
      {
        "json" : {
          "field" : "result",
          "strict_json_parsing": false,
          "add_to_root" : true
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
}<h2>Avaliações de indexação</h2><p>Agora, indexamos avaliações de produtos usando a API em lote. O pipeline criado anteriormente será aplicado automaticamente, adicionando a análise gerada pelo modelo Nova aos documentos indexados.</p>POST bulk/
{ "index": { "_index" : "products", "_id": 1, "pipeline":"review_analyzer_ai" } }
{ "product": "Pampers Pants Premium Care Fralda", "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks." }
{ "index": { "_index" : "products", "_id": 2, "pipeline":"review_analyzer_ai" } }
{ "product": "Portable Electric Body Massager", "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan." }
{ "index": { "_index" : "products", "_id": 3, "pipeline":"review_analyzer_ai" } }
{ "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset", "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible." }
{ "index": { "_index" : "products", "_id": 4, "pipeline":"review_analyzer_ai" } }
{ "product": "Air Fryer 4L Oil Free Fryer Mondial", "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk." }<h2>Consultar e analisar os resultados</h2><p>Por fim, executamos uma consulta para ver como o modelo Amazon Nova Lite analisa e classifica as avaliações. Ao executar o comando GET products/_search, obtemos os documentos já enriquecidos com os campos gerados a partir do conteúdo da avaliação.</p><p>O modelo identifica o sentimento predominante (positivo, neutro ou negativo), gera resumos concisos, extrai palavras-chave relevantes e estima a autenticidade de cada avaliação. Esses campos ajudam a entender a opinião do cliente sem precisar ler o texto completo.</p><p>Para interpretar os resultados, analisamos:</p><ul><li><p>O sentimento indica a percepção geral do consumidor em relação ao produto.</p></li><li><p>O resumo destaca os principais pontos mencionados.</p></li><li><p>Palavras-chave, que podem ser usadas para agrupar avaliações semelhantes ou identificar padrões de feedback.</p></li><li><p>Autenticidade, que indica se a avaliação parece confiável. Isso é útil para curadoria ou moderação.</p></li></ul>   "hits": [
      {
        "_index": "products",
        "_id": "1",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Pampers Pants Premium Care Fralda",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The reviewer praises the diaper for its great material, high cotton content, and leak-proof design, especially highlighting its effectiveness for their baby.",
            "sentiment": "positive",
            "keywords": [
              "best diaper",
              "great material",
              "cotton",
              "no plastic",
              "leak-proof",
              "baby",
              "effective"
            ],
            "authenticity": "authentic"
          },
          "review": "Best diaper ever! Great material, lots of cotton, without all that plastic. Doesn't leak! My baby is a boy and every diaper leaked around the waist, this model solved the problem. Even on a small baby it's worth the effort of putting on the short diaper. I put it on my baby at 9 pm and only take it off in the morning, without any leaks."
        }
      },
      {
        "_index": "products",
        "_id": "2",
        "_score": 1,
        "_source": {
          "product": "Portable Electric Body Massager",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product broke in three months for no apparent reason and the reviewer does not recommend it due to its short lifespan.",
            "sentiment": "negative",
            "keywords": [
              "broke",
              "short lifespan",
              "not recommend"
            ],
            "authenticity": "authentic"
          },
          "review": "It broke in three months for no apparent reason, thank goodness I didn't review it before. I don't recommend buying it because it has a short lifespan."
        }
      },
      {
        "_index": "products",
        "_id": "3",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Havit Fuxi-H3 Black Quad-Mode Wired and Wireless Gaming Headset",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The headset has good sound quality for the price but suffers from poor connectivity, especially when using the microphone or moving the headset. It also has compatibility issues with Linux.",
            "sentiment": "negative",
            "keywords": [
              "sound",
              "connectivity",
              "microphone",
              "compatibility",
              "annoying",
              "turn off and on",
              "Linux",
              "flexible stem",
              "work from home"
            ],
            "authenticity": "authentic"
          },
          "review": "The sound is good for the price, but the connectivity is horrible. You always need to be playing audio, otherwise it loses connection (I work from home, and this is very annoying). Sometimes it loses connection and you have to turn it off and on again to get it back on. The microphone is very sensitive, so it loses connection frequently and you have to turn the headset off and on for the microphone to work again. The flexibility of the stem is useless, because if you move it, the microphone can turn off. Sometimes I need to use Linux and the headset simply doesn't work. It's light and comfortable, the sound is adequate, but the connectivity is terrible."
        }
      },
      {
        "_index": "products",
        "_id": "4",
        "_score": 1,
        "_ignored": [
          "review.keyword"
        ],
        "_source": {
          "product": "Air Fryer 4L Oil Free Fryer Mondial",
          "model_id": "bedrock_completion_amazon_nova-lite",
          "review_analyze": {
            "summary": "The product offers value for money but has issues with peeling, rusting, and uneven frying.",
            "sentiment": "negative",
            "keywords": [
              "value for money",
              "peeling",
              "rusting",
              "uneven frying",
              "weaker in the middle"
            ],
            "authenticity": "authentic"
          },
          "review": "For those looking for value for money, it's a good option, but the tray (which is underneath the perforated basket) is already peeling a lot. My mother has one just like it and said that hers is even rusting, in other words, the material is MUCH inferior. There's also something that bothers me, because it looks like a microwave, it doesn't fry evenly, it's weaker in the middle and stronger on the sides. Buy at your own risk."
        }
      }
    ]<h2>Conclusão</h2><p>A integração entre o Amazon Nova Lite e o Elasticsearch demonstrou como os modelos de linguagem podem transformar avaliações brutas em informações estruturadas e valiosas. Ao processar as avaliações por meio de um pipeline, conseguimos extrair automaticamente e de forma consistente informações sobre sentimento, autenticidade, resumos e palavras-chave.</p><p>Os resultados mostram que o modelo consegue compreender o contexto das avaliações, classificar as opiniões dos usuários e destacar os pontos mais relevantes de cada experiência. Isso cria um conjunto de dados muito mais rico que pode ser aproveitado para melhorar as capacidades de pesquisa.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/amazon-nova-models-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbbf13eb690294f1/6a17fddd6df73195190a115a/304713c48b568e17d0bb56b19edb28769f7801b3-721x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 02 Apr 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Conecte agentes ao Elasticsearch com o protocolo de contexto do modelo]]></title>
    <description><![CDATA[Vamos usar o servidor Model Context Protocol para conversar com seus dados no Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>E se interagir com seus dados fosse tão fácil quanto conversar com um colega? Imagine simplesmente perguntar: "Mostre-me todos os pedidos acima de US$ 500 do mês passado" ou "Quais produtos receberam mais avaliações de 5 estrelas?" e obter respostas instantâneas e precisas, sem precisar fazer perguntas.</p><p>O Model Context Protocol (MCP) torna isso possível. Ele conecta perfeitamente a IA conversacional com seus bancos de dados e APIs externas, transformando solicitações complexas em conversas naturais. Embora os LLMs modernos sejam ótimos para entender a linguagem, seu verdadeiro potencial é revelado quando integrados a sistemas do mundo real. O MCP preenche a lacuna entre eles, tornando a interação de dados mais intuitiva e eficiente.</p><p>Nesta postagem, exploraremos:</p><ul><li><p>Arquitetura MCP – Como funciona nos bastidores</p></li><li><p>Benefícios de um servidor MCP conectado ao Elasticsearch</p></li><li><p>Construindo um <a href="https://github.com/elastic/mcp-server-elasticsearch">servidor MCP com tecnologia Elasticsearch</a></p></li></ul><p>Tempos emocionantes estão por vir! A integração do MCP com sua pilha Elastic transforma a maneira como você interage com as informações, tornando consultas complexas tão intuitivas quanto conversas cotidianas.</p><h2>Protocolo de Contexto do Modelo</h2><p><a href="https://modelcontextprotocol.io/introduction">O Model Context Protocol</a> (MCP), desenvolvido pela Anthropic, é um padrão aberto que conecta modelos de IA a fontes de dados externas por meio de canais bidirecionais seguros. Ele resolve uma grande limitação da IA: acesso em tempo real a sistemas externos, preservando o contexto da conversa.</p><h3>Arquitetura MCP</h3><p>A arquitetura do Protocolo de Contexto do Modelo consiste em dois componentes principais:</p><ul><li><p><strong>Clientes MCP</strong> – Assistentes de IA e chatbots que solicitam informações ou executam tarefas em nome dos usuários.</p></li><li><p><strong>Servidores MCP</strong> – Repositórios de dados, mecanismos de busca e APIs que recuperam informações relevantes ou executam ações solicitadas (por exemplo, chamar APIs externas).</p></li></ul><p>Os servidores MCP expõem quatro funcionalidades principais aos clientes:</p><ul><li><p><strong>Recursos</strong> - Dados estruturados, documentos e conteúdo que podem ser recuperados e usados como contexto para interações de LLM. Isso permite que assistentes de IA acessem informações relevantes de bancos de dados, índices de pesquisa ou outras fontes.</p></li><li><p><strong>Ferramentas</strong> - Funções executáveis que permitem que os LLMs interajam com sistemas externos, realizem cálculos ou tomem ações no mundo real. Essas ferramentas estendem os recursos de IA além da geração de texto, permitindo que os assistentes acionem fluxos de trabalho, chamem APIs ou manipulem dados dinamicamente.</p></li><li><p><strong>Prompts</strong> - Modelos de prompts e fluxos de trabalho reutilizáveis para padronizar e compartilhar interações comuns de LLM.</p></li><li><p><strong>Amostragem</strong> - Solicite conclusões de LLM por meio do cliente para permitir comportamentos de agente sofisticados, mantendo a segurança e a privacidade.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfe82754551bb187a/6a17f7ec6864a43e71b6895d/bef5178133391e96e3d66ae634e41a85712a33a9-2345x1620.png" alt="Arquitetura do Protocolo de Contexto do Modelo (MCP)" /><h2>Servidor MCP + Elasticsearch</h2><p></p><p>Os sistemas tradicionais de Recuperação-Geração Aumentada (RAG) recuperam documentos com base em consultas do usuário, mas o MCP vai um passo além: ele permite que agentes de IA construam e executem tarefas dinamicamente em tempo real. Isso permite que os usuários façam perguntas em linguagem natural como:</p><p></p><ul><li><p>"Mostre-me todos os pedidos acima de US$ 500 do mês passado."</p></li><li><p>"Quais produtos receberam mais avaliações de 5 estrelas?"</p></li></ul><p></p><p>E obtenha respostas instantâneas e precisas, sem precisar escrever uma única consulta.</p><p></p><p>O MCP consegue isso por meio de:</p><ul><li><p>Seleção dinâmica de ferramentas – Os agentes escolhem de forma inteligente as ferramentas certas expostas por meio de servidores MCP com base na intenção do usuário. LLMs “mais inteligentes” geralmente são melhores em selecionar as ferramentas certas com os argumentos apropriados com base no contexto.</p></li><li><p>Comunicação bidirecional – Agentes e fontes de dados trocam informações fluidamente, refinando consultas conforme necessário (por exemplo, primeiro mapeie o índice de pesquisa e só então construa a consulta ES).</p></li><li><p>Orquestração de múltiplas ferramentas – Os fluxos de trabalho podem aproveitar ferramentas de vários servidores MCP simultaneamente.</p></li><li><p>Contexto persistente – Os agentes lembram interações anteriores, mantendo a continuidade entre as conversas.</p></li></ul><p>Um servidor MCP conectado ao Elasticsearch desbloqueia uma poderosa arquitetura de recuperação em tempo real. Os agentes de IA podem explorar, consultar e analisar dados do Elasticsearch sob demanda. Seus dados podem ser pesquisados por meio de uma interface de bate-papo simples.</p><p>Além de apenas recuperar dados, o MCP possibilita ações. Ele se integra a outras ferramentas para acionar fluxos de trabalho, automatizar processos e fornecer insights aos sistemas de análise. Ao separar a pesquisa da execução, o MCP mantém os aplicativos com tecnologia de IA flexíveis, atualizados e perfeitamente integrados aos fluxos de trabalho do agente.</p><h2>Prático: servidor MCP para conversar com seus dados do Elasticsearch</h2><p>Para interagir com o Elasticsearch por meio de um servidor MCP, precisamos de pelo menos funções para:</p><ul><li><p>Recuperar índices</p></li><li><p>Obter mapeamentos</p></li><li><p>Realizar pesquisas usando o Query DSL do Elasticsearch</p></li></ul><p>Nosso servidor é escrito em TypeScript e usaremos o <a href="https://github.com/modelcontextprotocol/typescript-sdk">SDK oficial do MCP TypeScript</a>. Para configuração, recomendamos instalar o aplicativo Claude Desktop (a versão gratuita é suficiente), pois ele inclui um cliente MCP integrado. Nosso servidor MCP essencialmente expõe o <a href="https://www.elastic.co/pt/guide/en/elasticsearch/client/javascript-api/current/index.html">cliente oficial do JavaScript Elasticsearch</a> por meio de ferramentas MCP.</p><p>Vamos começar definindo o cliente Elasticsearch e o servidor MCP:</p> const esClient = new Client({
    node: url,
    auth: {
      apiKey: apiKey,
    },
  });

  const server = new McpServer({
    name: "elasticsearch-mcp-server",
    version: "0.1.0",
  });<p>Usaremos as seguintes ferramentas de servidor MCP que podem interagir com o Elasticsearch:</p><ul><li><p><strong>Listar índices</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L46">list_indices</a>): esta ferramenta recupera todos os índices disponíveis do Elasticsearch, fornecendo detalhes como nome do índice, status de integridade e contagem de documentos.</p></li><li><p><strong>Obter mapeamentos</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L94">get_mappings</a>): esta ferramenta busca os mapeamentos de campos para um índice especificado do Elasticsearch, ajudando os usuários a entender a estrutura e os tipos de dados dos documentos armazenados.</p></li><li><p><strong>Pesquisar</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L147">search</a>): Esta ferramenta executa uma pesquisa no Elasticsearch usando um DSL de consulta fornecido. Ele habilita automaticamente destaques para campos de texto, facilitando a identificação de resultados de pesquisa relevantes.</p></li></ul><p>A implementação completa do servidor Elasticsearch MCP está disponível no repositório <a href="https://github.com/elastic/mcp-server-elasticsearch">elastic/mcp-server-elasticsearch</a> .</p><h4>Converse com seu índice</h4><p>Vamos explorar como configurar o servidor Elasticsearch MCP para que você possa fazer perguntas em linguagem natural sobre seus dados, como "Encontrar todos os pedidos acima de US$ 500 do mês passado".</p><p><strong>Configure seu aplicativo Claude Desktop</strong></p><ul><li><p>Abra o aplicativo Claude Desktop</p></li><li><p>Navegue até Configurações &gt; Desenvolvedor &gt; Servidores MCP</p></li><li><p>Clique em "Editar configuração" e adicione esta configuração ao seu <code>claude_desktop_config.json</code>:</p></li></ul>{
  "mcpServers": {
    "Elasticsearch MCP Server": {
      "command": "npx",
      "args": [
        "-y",
        "@elastic/mcp-server-elasticsearch"
      ],
      "env": {
        "ES_URL": "",
        "ES_API_KEY": ""
      }
    }
  }
}<p>Observação: esta configuração utiliza o pacote npm <a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a> publicado pela Elastic. Se você quiser desenvolver localmente, poderá encontrar mais detalhes sobre como configurar o servidor Elasticsearch MCP <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md">aqui</a>.</p><p><strong>Preencha seu índice Elasticseach</strong></p><ul><li><p>Você pode usar nossos <a href="https://gist.github.com/jedrazb/60e9400cbe40addfd9e4337749c28431">dados de exemplo</a> para preencher o índice de "pedidos" para esta demonstração</p></li><li><p>Isso permitirá que você tente consultas como "Encontrar todos os pedidos acima de US$ 500 do mês passado"</p></li></ul><p><strong>Comece a usar</strong></p><ul><li><p>Abra uma nova conversa no aplicativo Claude Desktop</p></li><li><p>O servidor MCP se conectará automaticamente</p></li><li><p>Comece a fazer perguntas sobre seus dados do Elasticsearch!</p></li></ul><p>Confira esta demonstração para ver como é fácil consultar seus dados do Elasticsearch usando linguagem natural:</p><h4>Como funciona?</h4><p>Quando perguntado "Encontre todos os pedidos acima de US$ 500 do mês passado", o LLM reconhece a intenção de pesquisar o índice do Elasticsearch com restrições especificadas. Para realizar uma busca eficaz, o agente deve:</p><ul><li><p>Descubra o nome do índice: <code>orders</code></p></li><li><p>Entenda os mapeamentos do índice <code>orders</code></p></li><li><p>Crie o DSL de consulta compatível com mapeamentos de índice e, finalmente, execute a solicitação de pesquisa</p></li></ul><p>Essa interação pode ser representada como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt152f41bc8c3e9752/6a17f7ee6df73152df0a10cc/8875bc75745124be87deac0be666509446887de2-2345x1620.png" alt="Como funciona o servidor MCP + Elasticsearch" /><h2>Conclusão</h2><p>O Model Context Protocol aprimora a maneira como você interage com os dados do Elasticsearch, permitindo conversas em linguagem natural em vez de consultas complexas. Ao unir recursos de IA com seus dados, o MCP cria um fluxo de trabalho mais intuitivo e eficiente que mantém o contexto em todas as suas interações.</p><p>O servidor Elasticsearch MCP está disponível como um pacote npm público (<a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a>), tornando a integração simples para desenvolvedores. Com configuração mínima, sua equipe pode começar a explorar dados, acionar fluxos de trabalho e obter insights por meio de conversas simples.</p><p>Pronto para experimentar isso você mesmo? Experimente o <a href="https://github.com/elastic/mcp-server-elasticsearch">servidor Elasticsearch MCP</a> hoje mesmo e comece a conversar com seus dados.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce68a95c633809ae/6a17f7f0148009fa28b48915/65b378f644bd13e3edf2f108d48186f1889f546c-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construindo um sistema RAG multimodal com Elasticsearch: A história de Gotham City]]></title>
    <description><![CDATA[Aprenda a construir um sistema multimodal de Recuperação-Geração Aumentada (RAG) que integra dados de texto, áudio, vídeo e imagem para fornecer recuperação de informações mais rica e contextualizada.]]></description>
    <content:encoded><![CDATA[<p>Neste blog, você aprenderá a criar um pipeline RAG (Retrieval-Augmented Generation) multimodal usando o Elasticsearch. Exploraremos como aproveitar o ImageBind para gerar incorporações para vários tipos de dados, incluindo texto, imagens, áudio e mapas de profundidade. Você também descobrirá como armazenar e recuperar esses embeddings de forma eficiente no Elasticsearch usando dense_vector e pesquisa k-NN. Por fim, integraremos um grande modelo de linguagem (LLM) para analisar as evidências recuperadas e gerar um relatório final abrangente.</p><h3>Como funciona o gasoduto multimodal RAG?</h3><ol><li><p><strong>Coletando pistas</strong> → Imagens, áudio, textos e mapas de profundidade da cena do crime em Gotham.</p></li><li><p><strong>Gerando embeddings</strong> → Cada arquivo é convertido em um vetor usando o modelo multimodal ImageBind.</p></li><li><p><strong>Indexação no Elasticsearch</strong> → Os vetores são armazenados para recuperação eficiente.</p></li><li><p><strong>Pesquisando por similaridade</strong> → Dada uma nova pista, os vetores mais semelhantes são recuperados.</p></li><li><p><strong>O LLM analisa as evidências</strong> → Um modelo GPT-4 sintetiza a resposta e identifica o suspeito!</p></li></ol><h3>Tecnologias utilizadas</h3><ul><li><p><strong>ImageBind</strong> → Gera embeddings unificados para várias modalidades.</p></li><li><p><strong>Elasticsearch</strong> → Permite pesquisa vetorial rápida e eficiente.</p></li><li><p><strong>LLM (GPT-4, OpenAI)</strong> → Analisa as evidências e gera um relatório final.</p></li></ul><h3>Para quem é este blog?</h3><ul><li><p>Usuários do Elastic interessados em pesquisa vetorial multimodal.</p></li><li><p>Desenvolvedores que buscam entender o RAG Multimodal na prática.</p></li><li><p>Qualquer pessoa em busca de soluções escaláveis para analisar dados de diversas fontes.</p></li></ul><h2>Pré-requisitos para RAG multimodal: Configurando o ambiente</h2><p>Para resolver o crime em Gotham City, você precisa configurar seu ambiente tecnológico. Siga este guia passo a passo:</p><h3>1. Requisitos técnicos</h3><p>Componente</p><p>Especificação</p><p>Sistema Operacional</p><p>Linux, macOS ou Windows</p><p>Python</p><p>3.10 ou posterior</p><p>BATER</p><p>Mínimo de 8 GB (16 GB recomendado)</p><p>GPU</p><p>Opcional, mas recomendado para ImageBind</p><h3><strong>2. Configurando o projeto</strong></h3><p>Todos os materiais de investigação estão disponíveis no GitHub, e usaremos o Jupyter Notebook (Google Colab) para esta experiência interativa de resolução de crimes. Siga estes passos para começar:</p><h4>Configurando com o Jupyter Notebook (Google Colab)</h4><p><strong>1. Acesse o notebook</strong></p><ul><li><p>Abra nosso notebook Google Colab pronto para uso: <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham/notebook/01-mmrag-blog-quick-start.ipynb">Multimodal RAG com Elasticsearch</a><u>.</u></p></li><li><p>Este caderno contém todo o código e explicações que você precisa acompanhar.</p></li></ul><p><strong>2. Clone o repositório</strong></p># Clone the repository with the multimodal RAG code
!git clone -b https://github.com/elastic/elasticsearch-labs.git

# Navigate to the project directory
cd elasticsearch-labs/supporting-blog-content/building-multimodal-rag-with-elasticsearch-gotham<p><strong>3. Instalar dependências</strong></p> # Install PyTorch and related libraries
!pip install torch&gt;=2.1.0 torchvision&gt;=0.16.0 torchaudio&gt;=2.1.0

# Install vision processing libraries
!pip install opencv-python-headless pillow numpy

# Install the specific ImageBind fork
!pip install git+https://github.com/hkchengrex/ImageBind.git

# Install Elasticsearch and environment management
!pip install elasticsearch python-dotenv<p><strong>4. Configurar credenciais</strong></p># Input your credentials securely
import getpass

ELASTICSEARCH_URL = input("Enter the Elasticsearch endpoint url: ")
ELASTICSEARCH_API_KEY = getpass.getpass("Enter the Elasticsearch API key: ")
OPENAI_API_KEY = getpass.getpass("Enter the OpenAI API key: ")

# Configure environment variables
import os
os.environ["ELASTICSEARCH_API_KEY"] = ELASTICSEARCH_API_KEY
os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["ELASTICSEARCH_URL"] = ELASTICSEARCH_URL<p>Observação: o modelo ImageBind (~2 GB) será baixado automaticamente na primeira execução.</p><p>Agora que tudo está pronto, vamos nos aprofundar nos detalhes e solucionar o crime!</p><h2>Introdução: O crime em Gotham City</h2><p>Em uma noite chuvosa em Gotham City, um crime chocante abala a cidade. O comissário Gordon precisa da sua ajuda para desvendar o mistério. As pistas estão espalhadas em diferentes formatos: imagens borradas, áudio misterioso, textos criptografados e até mapas de profundidade. Você está pronto para usar a tecnologia de IA mais avançada para resolver o caso?</p><p>Neste blog, você será guiado passo a passo pela construção de um <strong>sistema RAG (Recuperação-Geração Aumentada) multimodal</strong> que unifica diferentes tipos de dados (<strong>imagens, áudio, textos e mapas de profundidade</strong>) em um único espaço de busca. Usaremos <strong>o ImageBind</strong> para gerar embeddings multimodais, <strong>o Elasticsearch</strong> para armazenar e recuperar esses embeddings e um <strong>Large Language Model (LLM)</strong> para analisar as evidências e gerar um relatório final.</p><h2>Fundamentos: Arquitetura RAG multimodal</h2><h3>O que é um RAG multimodal?</h3><p>A ascensão da <strong>Geração Aumentada de Recuperação (RAG) Multimodal</strong> está revolucionando a maneira como interagimos com modelos de IA. Tradicionalmente, os sistemas RAG trabalham exclusivamente com texto, recuperando informações relevantes de bancos de dados antes de gerar respostas. No entanto, o mundo não se limita ao texto<strong>: imagens, vídeos e áudio também contêm conhecimento valioso</strong>. É por isso que as arquiteturas multimodais estão ganhando destaque, permitindo que os sistemas de IA <strong>combinem informações de diferentes formatos para respostas mais ricas e precisas</strong>.</p><h3><strong>Três abordagens principais para RAG multimodal</strong></h3><p>Para implementar um RAG multimodal, três estratégias são comumente usadas. Cada abordagem tem suas próprias vantagens e limitações, dependendo do caso de uso:</p><h4>1. Espaço vetorial compartilhado</h4><p>Dados de diferentes modalidades são mapeados em um espaço vetorial comum usando modelos multimodais como o ImageBind. Isso permite que consultas de texto recuperem imagens, vídeos e áudio sem conversão de formato explícita.</p><p><strong>Vantagens:</strong></p><ul><li><p>Permite <strong>recuperação multimodal</strong> sem exigir conversão de formato explícita.</p></li><li><p>Fornece uma <strong>integração fluida</strong> entre diferentes modalidades, permitindo recuperação direta de texto, imagem, áudio e vídeo.</p></li><li><p>Escalável para diversos tipos de dados, o que o torna útil para <strong>aplicações de recuperação em larga escala</strong>.</p></li></ul><p><strong>Desvantagens:</strong></p><ul><li><p><strong>O treinamento requer grandes conjuntos de dados multimodais</strong>, que podem nem sempre estar disponíveis.</p></li><li><p>O espaço de incorporação compartilhado pode introduzir <strong>deriva semântica</strong>, onde as relações entre modalidades não são perfeitamente preservadas.</p></li><li><p><strong>O viés em modelos multimodais</strong> pode afetar a precisão da recuperação, dependendo da distribuição do conjunto de dados.</p></li></ul><h4>2. Modalidade de aterramento único</h4><p>Todas as modalidades são convertidas em um <strong>único formato</strong>, geralmente <strong>texto</strong>, antes da recuperação. Por exemplo, as imagens são descritas por meio de <strong>legendas geradas automaticamente</strong> e o áudio é transcrito em texto.</p><p><strong>Vantagens:</strong></p><ul><li><p><strong>Simplifica a recuperação</strong>, pois tudo é convertido em uma <strong>representação de texto uniforme</strong>.</p></li><li><p>Funciona bem com <strong>mecanismos de busca baseados em texto existentes</strong>, eliminando a necessidade de infraestrutura multimodal especializada.</p></li><li><p>Pode melhorar <strong>a interpretabilidade,</strong> pois os resultados recuperados estão em um formato legível por humanos.</p></li></ul><p><strong>Desvantagens:</strong></p><ul><li><p><strong>Perda de informações</strong>: certos detalhes (por exemplo, relações espaciais em imagens, tom em áudio) podem não ser totalmente capturados em descrições de texto.</p></li><li><p><strong>Depende da qualidade da legenda/transcrição</strong>: erros em anotações automáticas podem reduzir a eficácia da recuperação.</p></li><li><p><strong>Não é ideal para consultas puramente visuais ou auditivas,</strong> pois o processo de conversão pode remover contexto essencial.</p></li></ul><h4>3. Recuperação separada</h4><p>Mantém <strong>modelos distintos</strong> para cada modalidade. O sistema realiza <strong>pesquisas separadas</strong> para cada tipo de dado e depois <strong>mescla os resultados</strong>.</p><p><strong>Vantagens:</strong></p><ul><li><p>Permite <strong>otimização personalizada por modalidade</strong>, melhorando a precisão da recuperação para cada tipo de dado.</p></li><li><p>Menor dependência de <strong>modelos multimodais complexos</strong>, facilitando a integração de sistemas de recuperação existentes.</p></li><li><p>Fornece <strong>controle refinado sobre classificação e reclassificação,</strong> pois resultados de diferentes modalidades podem ser combinados dinamicamente.</p></li></ul><p><strong>Desvantagens:</strong></p><ul><li><p><strong>Requer fusão de resultados</strong>, tornando o processo de recuperação e classificação mais complexo.</p></li><li><p>Pode gerar <strong>respostas inconsistentes</strong> se modalidades diferentes retornarem informações conflitantes.</p></li><li><p><strong>Maior custo computacional</strong> , pois são realizadas buscas independentes para cada modalidade, aumentando o tempo de processamento.</p></li></ul><h3>Nossa escolha: Espaço vetorial compartilhado com ImageBind</h3><p>Dentre essas abordagens, escolhemos <strong>o espaço vetorial compartilhado</strong>, uma estratégia que se alinha perfeitamente com a necessidade de <strong>buscas multimodais eficientes</strong>. Nossa implementação é baseada no <strong>ImageBind</strong>, um modelo capaz de representar múltiplas modalidades (<strong>texto, imagem, áudio e vídeo</strong>) em um <strong>espaço vetorial comum</strong>. Isso nos permite:</p><ul><li><p>Realize <strong>pesquisas multimodais</strong> entre diferentes formatos de mídia sem precisar converter tudo em texto.</p></li><li><p>Use <strong>incorporações altamente expressivas</strong> para capturar relacionamentos entre diferentes modalidades.</p></li><li><p>Garanta <strong>escalabilidade e eficiência</strong>, armazenando embeddings otimizados para recuperação rápida no Elasticsearch.</p></li></ul><p>Ao adotar essa abordagem, construímos um <strong>pipeline de pesquisa multimodal robusto</strong>, onde uma consulta de texto pode <strong>recuperar imagens ou áudio diretamente</strong> sem pré-processamento adicional. Este método expande aplicações práticas de <strong>busca inteligente em grandes repositórios</strong> para <strong>sistemas avançados de recomendação multimodal</strong>.</p><p>A figura a seguir ilustra o fluxo de dados dentro do pipeline RAG multimodal, destacando o processo de indexação, recuperação e geração de resposta com base em dados multimodais:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77bce4aa5216bcf3/6a17eef263173069e0585b57/a4ffdb44582738991813c045be37312dacb0d4f3-1488x1436.png" alt="Fluxo de dados multimodal" /><h3>Como funciona o espaço de incorporação?</h3><p>Tradicionalmente, as incorporações de texto vêm de modelos de linguagem (por exemplo, BERT, GPT). Agora, com modelos multimodais nativos como <strong>o ImageBind</strong> da Meta AI, temos uma estrutura que gera vetores para múltiplas modalidades:</p><ul><li><p><strong>Texto</strong>: Frases e parágrafos são transformados em vetores da mesma dimensão.</p></li><li><p><strong>Imagens (visão)</strong>: Os pixels são mapeados no mesmo espaço dimensional usado para texto.</p></li><li><p><strong>Áudio</strong>: Os sinais sonoros são convertidos em incorporações comparáveis a imagens e texto.</p></li><li><p><strong>Mapas de profundidade</strong>: os dados de profundidade são processados e também resultam em vetores.</p></li></ul><p>Assim, qualquer pista (<strong>texto, imagem, áudio, profundidade</strong>) pode ser comparada a qualquer outra usando métricas de similaridade vetorial, como <strong>similaridade de cosseno</strong>. Se uma <strong>amostra de áudio de riso</strong> e uma <strong>imagem do rosto de um suspeito</strong> estiverem “próximas” neste espaço, podemos inferir alguma correlação (por exemplo, a mesma identidade).</p><h2>Etapa 1 - Coletando pistas da cena do crime</h2><p>Antes de analisar as evidências, precisamos coletá-las. O crime em Gotham deixou rastros que podem estar escondidos em imagens, áudios, textos e até mesmo dados de profundidade. Vamos organizar essas pistas para alimentá-las em nosso sistema.</p><h3>O que temos?</h3><p>O Comissário Gordon nos enviou os seguintes arquivos contendo evidências coletadas na cena do crime em quatro modalidades diferentes:</p><p><strong>Descrição e modalidade da pista</strong></p><p><strong>a) Imagens (2 fotos)</strong></p><ul><li><p><code>crime_scene1.jpg, crime_scene2.jpg</code> → Fotos tiradas da cena do crime. Mostra vestígios suspeitos no chão.</p></li><li><p><code>suspect_spotted.jpg</code> → Imagem de câmera de segurança mostrando uma silhueta fugindo do local.</p></li></ul><p><strong>b)</strong> <strong>Áudio (1 gravação)</strong></p><ul><li><p><code>joker_laugh.wav </code>→ Um microfone perto da cena do crime captou uma risada sinistra.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7690380dc545367/6a17eef8be6086886a00483b/3457bab38aa4a3caf61ca2a5e0a8b477ddd15cb1-86x45.png" alt="" /><p><strong>c) Texto (1 mensagem)</strong></p><ul><li><p><code>Riddle.txt, note2.txt</code> → Algumas notas misteriosas foram encontradas no local, possivelmente deixadas pelo criminoso.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8e5b8f4bc4124708/6a17eefa1d1b83850c93e50c/8963228dc3b4b7e2106cb6ddffbe2154020eb735-77x79.png" alt="" /><p><strong>d) Profundidade (1 mapa de profundidade)</strong></p><ul><li><p><code>depth_suspect.png</code> → Uma câmera de segurança com sensor de profundidade capturou um suspeito em um beco próximo.</p></li><li><p><code>jdancing-depth.png</code> → Uma câmera de segurança com sensor de profundidade capturou um suspeito descendo a estação de metrô.</p></li></ul><p>Essas evidências estão em formatos diferentes e não podem ser analisadas diretamente da mesma maneira. Precisamos transformá-los em embeddings — vetores numéricos que permitirão comparação entre modais.</p><h3><strong>Organização de arquivos</strong></h3><p>Antes de iniciar o processamento, precisamos garantir que todas as pistas estejam organizadas corretamente no diretório data/ para que o pipeline funcione sem problemas.</p><p><strong>Estrutura de diretório esperada:</strong></p>data/
├── images/
│   ├── crime_scene1.jpg
│   ├── suspect_spotted.jpg
│   ...
├── audios/
│   ├── joker_laugh.wav
│   ...
├── texts/
│   ├── riddle.txt
│   ... 
├── depths/
│   ├── depth_suspect.png<h3>Código para verificar a organização da pista</h3><p>Antes de prosseguir, vamos garantir que todos os arquivos necessários estejam no local correto.</p>import os

# Base directory for clues
data_dir = "data"

# List of expected files
evidences = {
    "images": ["crime_scene1.jpg","crime_scene1.jpg", "joker_alley.jpg"],
    "audios": ["joker_laugh.wav"],
    "texts": ["riddle.txt", "note2.txt”],
    "depths": ["depth_suspect.png", "jdancing-depth.png"]
}

# Create directories if they don't exist
for category, files in evidences.items():
    category_path = os.path.join(data_dir, category)
    os.makedirs(category_path, exist_ok=True)

    for file in files:
        file_path = os.path.join(category_path, file)
        if not os.path.exists(file_path):
            print(f"Warning: {file} not found in {category_path}.")

print("All files are correctly organized!")<p><strong>Executando o arquivo</strong></p>python  stages/01-stage/files_check.py<p><strong>Saída esperada (se todos os arquivos estiverem corretos):</strong></p>All files are correctly organized!<p><strong>Saída esperada (se algum arquivo estiver faltando):</strong></p>Warning: joker_laugh.wav not found in data/audios/
Warning: depth_suspect.png not found in data/depths/<p>Este script ajuda a evitar erros antes de começarmos a gerar embeddings e indexá-los no Elasticsearch.</p><h2>Etapa 2 - Organizando as evidências</h2><h3>Gerando embeddings com ImageBind</h3><p>Para unificar as pistas, precisamos transformá-las em embeddings — representações vetoriais que capturam o significado de cada modalidade. Usaremos <strong>o ImageBind</strong>, um modelo da Meta AI que gera embeddings para diferentes tipos de dados (<strong>imagens, áudio, texto e mapas de profundidade</strong>) dentro de um espaço vetorial compartilhado.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff4aefffbfb5bf00/6a17eefe6864a45aecb68860/b19a1c32cc7a0b4c00fa5b247b18cce71f9693cb-1580x918.png" alt="Gerando embeddings com ImageBind" /><h3><strong>Como o ImageBind funciona?</strong></h3><p>Para comparar diferentes tipos de evidências (<strong>imagens, áudio, texto e mapas de profundidade</strong>), precisamos transformá-los em vetores numéricos usando <strong>o ImageBind</strong>. Este modelo permite que qualquer tipo de entrada seja convertido no mesmo formato de incorporação, permitindo <strong>pesquisas</strong> entre modalidades.</p><p>Abaixo está um código otimizado (<code>src/embedding_generator.py</code>) para gerar embeddings para qualquer tipo de entrada usando os processadores apropriados para cada modalidade:</p>class EmbeddingGenerator:
    """Class for generating multimodal embeddings using ImageBind."""
    
    def __init__(self):
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = self._load_model()

    def _load_model(self):
        """Loads the ImageBind model and sets it to inference mode."""
        model = imagebind_model.imagebind_huge(pretrained=True)
        model.eval()
        model.to(self.device)
        return model

    def generate_embedding(self, input_data, modality):
        """Generates embedding for different modalities"""
        processors = {
            "vision": lambda x: data.load_and_transform_vision_data(x, self.device),
            "audio": lambda x: data.load_and_transform_audio_data(x, self.device),
            "text": lambda x: data.load_and_transform_text(x, self.device),
            "depth": self.process_depth
        }
        
        try:
            # Input type verification
            if not isinstance(input_data, list):
                raise ValueError(f"Input data must be a list. Received: {type(input_data)}")
                
            # Convert input data to a tensor format that the model can process
            # For images: [batch_size, channels, height, width] 
            # For audio: [batch_size, channels, time] 
            # For text: [batch_size, sequence_length]
            inputs = {modality: processors[modality](input_data)}
            with torch.no_grad():
                embedding = self.model(inputs)[modality]
            return embedding.squeeze(0).cpu().numpy()
        except Exception as e:
            logger.error(f"Error generating {modality} embedding: {str(e)}", exc_info=True)
            raise<p>Um tensor é uma estrutura de dados fundamental em aprendizado de máquina e aprendizado profundo, especialmente ao trabalhar com modelos como o ImageBind. No nosso contexto:</p>input_tensor = processors[modality]([input_data], self.device)<p>Aqui, o tensor representa os dados de entrada (imagem, áudio ou texto) convertidos em um formato matemático que o modelo pode processar. Especificamente:</p><ul><li><p><strong>Para imagens</strong>: O tensor representa a imagem como uma matriz multidimensional de valores numéricos (pixels organizados por altura, largura e canais de cor).</p></li><li><p><strong>Para áudio</strong>: O tensor representa ondas sonoras como uma sequência de amplitudes ao longo do tempo.</p></li><li><p><strong>Para texto</strong>: O tensor representa palavras ou tokens como vetores numéricos.</p></li></ul><h3>Testando a geração de incorporação:</h3><p>Vamos testar nossa geração de incorporação com o código a seguir. Salve-o em 02-stage/test_embedding_generation.py e execute-o com este comando:</p>python stages/02-stage/test_embedding_generation.py generator = EmbeddingGenerator()
image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg","vision")

print(image_embedding.shape)<h3>Saída esperada:</h3>(1024,)<p>Agora, a imagem foi transformada em um <strong>vetor de 1024 dimensões</strong>.</p><h2>Etapa 3 - Armazenamento e pesquisa no Elasticsearch</h2><p>Agora que geramos os embeddings para as evidências, precisamos armazená-los em um banco de dados vetorial para permitir pesquisas eficientes. Para isso, usaremos <strong>o Elasticsearch</strong>, que suporta vetores densos (<code>dense_vector</code>) e permite buscas por similaridade.</p><p>Esta etapa consiste em dois processos principais:</p><ul><li><p><strong>Indexando os embeddings</strong> → Armazena os vetores gerados no Elasticsearch.</p></li><li><p><strong>Pesquisa de similaridade</strong> → Recupera os registros mais semelhantes a uma nova evidência.</p></li></ul><h3>Indexando as evidências no Elasticsearch</h3><p>Cada evidência processada pelo <strong>ImageBind</strong> (imagem, áudio, texto ou profundidade) é convertida em um <strong>vetor de 1024 dimensões</strong>. Precisamos armazenar esses vetores no <strong>Elasticsearch</strong> para permitir pesquisas futuras.</p><p>O código a seguir (<code>src/elastic_manager.py</code>) cria um <strong>índice</strong> no Elasticsearch e configura o mapeamento para armazenar os embeddings.</p>from elasticsearch import Elasticsearch, helpers
...

class ElasticsearchManager:
    """Manages multimodal operations in Elasticsearch"""
    
    def __init__(self):
        load_dotenv()  # Load variables from .env
        self.es = self._connect_elastic()
        self.index_name = "multimodal_content"
        self._setup_index()
    
    def _connect_elastic(self):
        """Connects to Elasticsearch"""
        return Elasticsearch(
            os.getenv("ELASTICSEARCH_URL"),  # Elasticsearch endpoint
            api_key=os.getenv("ELASTICSEARCH_API_KEY")
        )
    
    def _setup_index(self):
        """Sets up the index if it doesn't exist"""
        if not self.es.indices.exists(index=self.index_name):
            mapping = {
                "mappings": {
                    "properties": {
                        "embedding": {
                            "type": "dense_vector",
                            "dims": 1024,
                            "index": True,
                            "similarity": "cosine"
                        },
                        "modality": {"type": "keyword"},
                        "content": {"type": "binary"},
                        "description": {"type": "text"},
                        "metadata": {"type": "object"},
                        "content_path": {"type": "text"}
                    }
                }
            }
            self.es.indices.create(index=self.index_name, body=mapping)
    
    def index_content(self, embedding, modality, content=None, description="", metadata=None, content_path=None):
        """Indexes multimodal content"""
        doc = {
            "embedding": embedding.tolist(),
            "modality": modality,
            "description": description,
            "metadata": metadata or {},
            "content_path": content_path
        }
        
        if content:
            doc["content"] = base64.b64encode(content).decode() if isinstance(content, bytes) else content
        
        return self.es.index(index=self.index_name, document=doc)
    
    def search_similar(self, query_embedding, modality=None, k=5):
        """Searches for similar contents"""
        query = {
            "knn": {
                "field": "embedding",
                "query_vector": query_embedding.tolist(),
                "k": k,
                "num_candidates": 100,
                "filter": [{"term": {"modality": modality}}] if modality else []
            }
        }
        
        try:
            response = self.es.search(
                index=self.index_name,
                query=query,
                size=k            
            )
            
            # Return both source data and score for each hit
            return [{
                **hit["_source"],
                "score": hit["_score"]
            } for hit in response["hits"]["hits"]]
        
        except Exception as e:
            print(f"Error: processing search_evidence: {str(e)}")
            return "Error generating search evidence"<h3>Executando a indexação</h3><p>Agora, vamos indexar uma evidência para testar o processo.</p># Example: Indexing an image from the crime scene
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

image_embedding = generator.generate_embedding("data/images/crime_scene1.jpg", "vision")

response = es_manager.index_content(
    embedding=image_embedding,
    modality="vision",
    description="Photo of the crime scene with suspicious traces",
    content_path="data/images/crime_scene1.jpg"
)
print(json.dumps(response, indent=2))<p><strong>Saída esperada no Elasticsearch (resumo do documento indexado):</strong></p>{
    "embedding": [0.12, -0.53, 0.89, ...],  
    "modality": "vision",  
    "description": "Photo of the crime scene with suspicious traces",  
    "content_path": "data/images/crime_scene1.jpg"  
}<p>Para indexar todas as evidências multimodais, execute o seguinte comando Python:</p>python stages/03-stage/index_all_modalities.py<p>Agora, as evidências são armazenadas no <strong>Elasticsearch</strong> e estão prontas para serem recuperadas quando necessário.</p><h3>Verificando o processo de indexação</h3><p>Depois de executar o script de indexação, vamos verificar se todas as nossas evidências foram armazenadas corretamente no Elasticsearch. Você pode usar <strong>as ferramentas de desenvolvimento do Kibana</strong> para executar algumas consultas de verificação:</p><p>1. Primeiro, verifique se o índice foi criado:</p>GET _cat/indices/multimodal_content?v<p>2. Em seguida, verifique a contagem de documentos por modalidade:</p>GET multimodal_content/_search
{
  "size": 0,
  "aggs": {
    "modalities": {
      "terms": {
        "field": "modality.keyword"
      }
    }
  }
}<p>3. Por fim, examine a estrutura do documento indexado:</p>GET multimodal_content/_search
{
  "size": 1,
  "query": {
    "match_all": {}
  }
}<h4>Resultados esperados:</h4><ul><li><p>Um índice chamado `multimodal_content` deve existir.</p></li><li><p>Cerca de 7 documentos distribuídos em diferentes modalidades (visão, áudio, texto, profundidade).</p></li><li><p>Cada documento deve conter: campos de incorporação, modalidade, descrição, metadados e content_path.</p></li></ul><p>Esta etapa de verificação garante que nosso banco de dados de evidências esteja configurado corretamente antes de prosseguirmos com as pesquisas de similaridade.</p><h3>Procurando evidências semelhantes no Elasticsearch</h3><p>Agora que as evidências foram indexadas, podemos realizar pesquisas para encontrar os registros mais semelhantes a uma nova pista. Esta pesquisa usa <strong>similaridade vetorial</strong> para retornar os registros mais próximos no <strong>espaço de incorporação</strong>.</p><p>O código a seguir realiza essa pesquisa.</p>def search_similar_evidence(self, query_embedding, k=5, modality=None):
    """Performs a kNN search to find the most similar clues."""
    
    knn_query = {
        "field": "embedding",
        "query_vector": query_embedding.tolist(),
        "k": k,
        "num_candidates": 100
    }

    query_body = {"knn": knn_query}
    if modality:
        query_body = {
            "bool": {
                "must": [
                    query_body, 
                    {"term": {"modality": modality}}
                ]
            }
        }

    try:
      results = self.es.search(
        index=self.index_name,
        query=query_body,
        _source_includes=["description", "modality", "content_path"],
        size=k
      )
    except Exception as e:
            print(f"Error processing search_evidence: {str(e)}")
            return "Error generating search evidence”

    return results["hits"]["hits"]<h3>Testando a pesquisa - Usando áudio como consulta para resultados multimodais</h3><p>Agora, vamos testar a busca por evidências usando um <strong>arquivo de áudio suspeito</strong>. Precisamos gerar um embedding para o arquivo da mesma maneira e procurar por embeddings semelhantes:</p>python stages/03-stage/search_by_audio.py# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager(cloud_id="YOUR_CLOUD_ID", api_key="YOUR_API_KEY")

# Generate embedding for a suspicious audio
audio_embedding = generator.generate_embedding("data/audios/mysterious_laugh.wav", "audio")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar_evidence(audio_embedding, k=3)

# Display the retrieved results
print("\n🔎 Similar evidence found:\n")
for i, evidence in enumerate(similar_evidences, start=1):
    description = evidence['_source']['description']
    modality = evidence['_source']['modality']
    score = evidence['_score']
    content_path = evidence['_source'].get('content_path', 'N/A')

    print(f"{i}. {description} ({modality})")
    print(f"   Similarity: {score:.4f}")
    print(f"   File path: {content_path}\n")<p><strong>Saída esperada no terminal:</strong></p>🔎 Similar evidence found:

1. A sinister laugh captured near the crime scene (audio)
   Similarity: 0.9985
   File path: data/audios/joker_laugh.wav

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6068
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.5591
   File path: data/images/jdancing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><p>Agora, podemos <strong>analisar as evidências recuperadas</strong> e determinar sua relevância para o caso.</p><h3>Além do áudio - Explorando pesquisas multimodais</h3><h4>Invertendo os papéis: Qualquer modalidade pode ser uma "pergunta"</h4><p>Em nosso sistema <strong>RAG multimodal</strong> , <strong>cada modalidade</strong> é uma <strong>consulta de pesquisa</strong> potencial. Vamos além do exemplo de áudio e explorar como outros tipos de dados podem <strong>iniciar investigações</strong>.</p><h4>1. Busca por texto (decifrando a nota do criminoso)</h4><p>Cenário: Você encontrou uma <strong>mensagem de texto criptografada</strong> e quer encontrar evidências relacionadas.</p>python stages/03-stage/search_by_text.py# Generate embedding from text
text = "Why so serious?"
embedding_text = generator.generate_embedding([text], "text")

# Search for related evidence
similar_evidences = es_manager.search_similar(
    query_embedding=embedding_text,
    k=3
)<p><strong>Resultados esperados:</strong></p>🔎 Similar evidence found:

1. Mysterious note found at the location (text)
   Similarity: 0.7639
   File path: data/texts/riddle.txt

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.7161
   File path: data/images/joker_laughing.png

3. Why so serious (text)
   Similarity: 0.7132
   File path: data/texts/note2.txt<h4>2. Busca de imagens (rastreamento da cena suspeita do crime)</h4><p><strong>Cenário:</strong> Uma <strong>nova cena de crime</strong> (<code>crime_scene2.jpg</code>) precisa ser comparada com outras evidências.
</p>python stages/03-stage/search_by_image.py# Generate embedding for a suspicious image
vision_embedding = generator.generate_embedding(["data/images/crime_scene2.jpg"], "vision")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    k=3
)<p><strong>Saída:</strong></p>🔎 Similar evidence found:

1. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.8258
   File path: data/images/crime_scene1.jpg

2. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.6897
   File path: data/images/joker_laughing.png

3. Suspect dancing (vision)
   Similarity: 0.6588
   File path: data/images/jdancing.png<h4>3. Busca de mapa de profundidade (busca 3D)</h4><p><strong>Cenário:</strong> Um <strong>mapa de</strong> profundidade (<code>jdancing-depth.png</code>) revela <strong>padrões de escape de</strong> <strong>imagem</strong>.</p>python stages/03-stage/search_by_depth.py# Generate embedding for a suspicious depth map
vision_embedding = generator.generate_embedding(["data/depths/jdancing-depth.png"], "depth")

# Search for similar evidence in Elasticsearch
similar_evidences = es_manager.search_similar(
    query_embedding=vision_embedding,
    modality="vision",
    k=3
)<p><strong>Saída</strong></p>🔎 Similar evidence found:

1. The Joker with green hair, white face paint, and a sinister smile in an urban night setting. (vision)
   Similarity: 0.5329
   File path: data/images/joker_laughing.png<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt224eaf1be75a8b6b/6a17ef03e8fbceada23a1a0e/985f9536db95c772c696dfac996822fb00f199ee-1594x1160.png" alt="" /><p></p>2. Photo of the crime scene: A dark, rain-soaked alley is filled with playing cards, while a sinister graffiti of the Joker laughing stands out on the brick wall. (vision)
   Similarity: 0.5053
   File path: data/images/crime_scene1.jpg<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d75bdf986e1bbdd/6a17eef32f4a5c25d5fa89b1/7023ee786ccc760689257abbde2f759ca3cf5c59-1024x768.jpg" alt="" />3. Suspect dancing (vision)
   Similarity: 0.4859
   File path: data/images/jdancing.png<p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73f8f56b0f2b8493/6a17ef01faa91381ca93c94e/24067ea40f7958e171149221f83cbd9bcfccc53f-1582x1208.png" alt="" /><h3><strong>Por que isso importa?</strong></h3><p>Cada modalidade revela <strong>conexões únicas</strong>:</p><ul><li><p><strong>Texto</strong> → Padrões linguísticos do suspeito.</p></li><li><p><strong>Imagens</strong> → Reconhecimento de <strong>locais e objetos.</strong></p></li><li><p><strong>Profundidade</strong> → <strong>Reconstrução de cena 3D.</strong></p></li></ul><p>Agora, temos um <strong>banco de dados de evidências estruturado</strong> no <strong>Elasticsearch</strong>, o que nos permite <strong>armazenar e recuperar evidências multimodais de forma eficiente</strong>.</p><h3><strong>Resumo do que fizemos:</strong></h3><ul><li><p><strong>Embeddings multimodais armazenados</strong> no Elasticsearch.</p></li><li><p><strong>Realizou pesquisas de similaridade</strong>, encontrando evidências relacionadas a novas pistas.</p></li><li><p><strong>Testou a pesquisa usando um arquivo de áudio suspeito</strong>, garantindo que o sistema funcionasse corretamente.</p></li></ul><p><strong>Próximo passo:</strong> Usaremos um <strong>LLM</strong> (Large Language Model) para <strong>analisar as evidências recuperadas</strong> e gerar um <strong>relatório final</strong>.</p><h2>Etapa 4 - Conectando os pontos com o LLM</h2><p>Agora que as <strong>evidências foram indexadas</strong> no <strong>Elasticsearch</strong> e podem ser recuperadas por similaridade, precisamos de um <strong>LLM (Large Language Model)</strong> para <strong>analisá-</strong> las e gerar um <strong>relatório final</strong> para enviar ao Comissário Gordon. O <strong>LLM</strong> será responsável por <strong>identificar padrões, conectar pistas e sugerir um possível suspeito</strong> com base nas evidências recuperadas.</p><p>Para esta tarefa, usaremos <strong>o GPT-4 Turbo</strong>, formulando um <strong>prompt detalhado</strong> para que o modelo possa <strong>interpretar</strong> os resultados de forma eficiente.</p><h3><strong>Integração LLM</strong></h3><p>Para integrar o <strong>LLM</strong> em nosso sistema, criamos a classe <strong>LLMAnalyzer</strong> (<code>src/llm_analyzer.py</code>), que recebe as <strong>evidências recuperadas</strong> do <strong>Elasticsearch</strong> e gera um <strong>relatório forense</strong> usando essas evidências como contexto de prompt.</p>import os
from openai import OpenAI
import logging
from dotenv import load_dotenv

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class LLMAnalyzer:
    """Evidence analyzer using GPT-4"""
    
    def __init__(self):
        load_dotenv()
        self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    
    def analyze_evidence(self, evidence_results):
        """
        Analyzes multimodal search results and generates a report
        
        Args:
            evidence_results: Dict with results by modality
            {
                'vision': [...],
                'audio': [...],
                'text': [...],
                'depth': [...]
            }
        """
        # Format evidence for the prompt
        evidence_summary = self._format_evidence(evidence_results)

        # final prompt
        prompt = f"""
You are a highly experienced forensic detective specializing in multimodal evidence analysis. Your task is to analyze the collected evidence (audio, images, text, depth maps) and conclusively determine the **prime suspect** responsible for the Gotham Central Bank case.

---

### **Collected Evidence:**
{evidence_summary}

### **Task:**
1. **Analyze all the evidence** and identify cross-modal connections.
2. **Determine the exact identity of the criminal** based on behavioral patterns, visual/auditory/textual clues, and symbolic markers.
3. **Justify your conclusion** by explaining why this suspect is definitively responsible.
4. **Assign a confidence score (0-100%)** to your conclusion.

---

### **Final Output Format (Strictly Follow This Format):**
- **Prime Suspect:** [Full Name or Alias]
- **Evidence Supporting Conclusion:** [Detailed breakdown of visual, auditory, textual, and behavioral evidence]
- **Behavioral Patterns:** [Key actions, motives, and criminal signature]
- **Confidence Level:** [0-100%]
- **Next Steps (if any):** [What additional evidence would further confirm the identity? If none, state "No further evidence required."]

If there is **insufficient evidence**, specify exactly what is missing and suggest what additional data would be needed for a conclusive identification.

This report must be **direct and definitive**--avoid speculation and provide a final, actionable determination of the suspect's identity.
"""
        try:
            response = self.client.chat.completions.create(
                model="gpt-4-turbo-preview",
                messages=[
                    {
                        "role": "system",
                        "content": "You are a forensic detective specialized in multimodal evidence analysis."
                    },
                    {"role": "user", "content": prompt_01}
                ],
                temperature=0.5,
                max_tokens=1000
            )
            
            report = response.choices[0].message.content
            logger.info("\n📋 Forensic Report Generated:")
            logger.info("=" * 50)
            logger.info(report)
            logger.info("=" * 50)
            
            return report
            
        except Exception as e:
            logger.error(f"Error generating report: {str(e)}")
            return None<h4>Configuração de temperatura na análise LLM:</h4><p>Para nosso sistema de análise forense, usamos uma temperatura moderada de 0,5. Este cenário equilibrado foi escolhido porque:</p><ul><li><p>Representa um meio termo entre saídas determinísticas (muito rígidas) e altamente aleatórias;</p></li><li><p>Em 0,5, o modelo mantém estrutura suficiente para fornecer conclusões forenses lógicas e justificáveis;</p></li><li><p>Essa configuração permite que o modelo identifique padrões e faça conexões, permanecendo dentro de parâmetros razoáveis de análise forense;</p></li><li><p>Ele equilibra a necessidade de resultados consistentes e confiáveis com a capacidade de gerar análises criteriosas.</p></li></ul><p>Essa configuração de temperatura moderada ajuda a garantir que nossa análise forense seja confiável e criteriosa, evitando conclusões excessivamente rígidas e especulativas.</p><h3>Executando a análise de evidências</h3><p>Agora que temos a <strong>integração do LLM</strong>, precisamos de um <strong>script</strong> que conecte todos os componentes do sistema. Este script irá:</p><ul><li><p><strong>Pesquise evidências semelhantes</strong> no <strong>Elasticsearch.</strong></p></li><li><p><strong>Analise as evidências recuperadas</strong> usando o <strong>LLM</strong> para gerar um <strong>relatório final.</strong></p></li></ul><h4>Código: Script de análise de evidências</h4>python stages/04-stage/rag_crime_analyze.pyimport sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'src'))

from embedding_generator import EmbeddingGenerator
from elastic_manager import ElasticsearchManager
from llm_analyzer import LLMAnalyzer

import json
import logging
from dotenv import load_dotenv

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

# Initialize classes
generator = EmbeddingGenerator()
es_manager = ElasticsearchManager()

llm = LLMAnalyzer()
logger.info("✅ All components initialized successfully")
    
try:
    evidence_data = {}
    
    # Get data for each modality
    test_files = {
        'vision': 'data/images/crime_scene2.jpg',
        'audio': 'data/audios/joker_laugh.wav',
        'text': 'Why so serious?',
        'depth': 'data/depths/jdancing-depth.png'
    }
    
    logger.info("🔍 Collecting evidence...")
    for modality, test_input in test_files.items():
        try:
            if modality == 'text':
                embedding = generator.generate_embedding([test_input], modality)
            else:
                embedding = generator.generate_embedding([str(test_input)], modality)
            
            results = es_manager.search_similar(embedding, k=2)
            if results:
                evidence_data[modality] = results
                logger.info(f"✅ Data retrieved for {modality}: {len(results)} results")
            else:
                logger.warning(f"⚠️ No results found for {modality}")
                
        except Exception as e:
            logger.error(f"❌ Error retrieving {modality} data: {str(e)}")
    
    if not evidence_data:
        raise ValueError("No evidence data found in Elasticsearch!")
    
    # Test forensic report generation
    logger.info("\n📝 Generating forensic report...")
    report = llm.analyze_evidence(evidence_data)
    
    if report:
        logger.info("✅ Forensic report generated successfully")
        logger.info("\n📊 Report Preview:")
        logger.info("+" * 50)
        logger.info(report)
        logger.info("+" * 50)
    else:
        raise ValueError("Failed to generate forensic report")
        
except Exception as e:
    logger.error(f"❌ Error in analysis : {str(e)}")<h4>Saída esperada do LLM</h4>**Prime Suspect:** The Joker

**Evidence Supporting Conclusion:**

- **Visual Evidence:**
  - The photo of the crime scene with playing cards scattered around and the graffiti of the Joker laughing matches the Joker's known calling cards and thematic elements. The similarity score of 0.83 indicates a high likelihood that these elements are directly associated with the Joker.
  - The image of the Joker with green hair, white face paint, and a sinister smile in an urban night setting, although with a lower similarity score of 0.69, still supports the presence or recent activity of the Joker in areas consistent with the crime scene's characteristics.

- **Auditory Evidence:**
  - The captured sinister laugh with a similarity score of 1.00 perfectly matches known audio profiles of the Joker, making it a direct auditory signature of his presence at or near the crime scene.
  - Despite the lower similarity score of 0.61, the second audio piece further corroborates the Joker's involvement through thematic consistency.

- **Textual Evidence:**
  - The mysterious note found at the location, with a similarity score of 0.76, likely contains thematic or direct references to the Joker's modus operandi or signature phrases, further implicating him in the crime.
  - The similarity score of 0.72 for the Joker's description in textual evidence reinforces the thematic connection to the crime scene.

- **Depth Evidence:**
  - Depth sensor capture of the suspect with a similarity score of 0.77 suggests a physical presence matching the Joker's known dimensions or characteristic movements.
  - The lower similarity score of 0.53 in the second depth evidence still contributes to the overall pattern of evidence pointing towards the Joker, albeit with less certainty.

**Behavioral Patterns:**
- The Joker is known for his theatrical crimes, often leaving behind a signature trail of chaos, including playing cards, sinister laughter, and thematic graffiti. These elements are not only consistent with his known criminal signature but also directly observed at the crime scene.
- His motives often include creating chaos, drawing attention to his acts, and challenging his arch-nemesis, Batman, making a high-profile bank heist fitting within his behavioral patterns.

**Confidence Level:** 95%

**Next Steps:** No further evidence required.

The combination of visual, auditory, textual, and depth evidence strongly points to the Joker as the prime suspect. The thematic consistency across multiple modes of evidence, combined with known behavioral patterns and criminal signature, leaves little doubt regarding his involvement. While there is always a small margin of uncertainty in forensic analysis, the evidence at hand provides a compelling case against the Joker with a high degree of confidence.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt05526b73530f46ed/6a17ef043e03d782434f2d30/132ee0880b7fb1e64b5b2d886ab76b58baa6de37-1024x768.jpg" alt="" /><h2>Conclusão: Caso resolvido</h2><p>Com todas as <strong>pistas reunidas e analisadas</strong>, o <strong>sistema Multimodal RAG</strong> identificou um suspeito: <strong>o Coringa</strong>.</p><p>Ao combinar <strong>imagens, áudio, texto e mapas de profundidade</strong> em um <strong>espaço vetorial compartilhado</strong> usando <strong>o ImageBind</strong>, o sistema foi capaz de <strong>detectar conexões</strong> que seriam impossíveis de identificar manualmente. <strong>O Elasticsearch</strong> garantiu <strong>buscas rápidas e eficientes</strong>, enquanto o <strong>LLM</strong> sintetizou as evidências em um <strong>relatório claro e conclusivo</strong>.</p><p>No entanto, o <strong>verdadeiro poder</strong> deste sistema <strong>vai além de Gotham City</strong>. A <strong>arquitetura Multimodal RAG</strong> abre portas para <strong>inúmeras aplicações do mundo real</strong>:</p><ul><li><p><strong>Vigilância urbana:</strong> Identificação de suspeitos com base em <strong>imagens, áudio e dados de sensores</strong>.</p></li><li><p><strong>Análise forense:</strong> Correlacionando <strong>evidências de múltiplas fontes</strong> para solucionar <strong>crimes complexos</strong>.</p></li><li><p><strong>Recomendação multimídia:</strong> Criação de <strong>sistemas de recomendação</strong> que entendam <strong>contextos multimodais</strong> (por exemplo, sugerir <strong>músicas</strong> com base em imagens ou texto).</p></li><li><p><strong>Tendências de mídia social:</strong> detectando <strong>tópicos de tendência</strong> em diferentes formatos de dados.</p></li></ul><p>Agora que você aprendeu a <strong>construir um sistema RAG multimodal</strong>, por que não <strong>testá-lo com suas próprias dicas</strong>?</p><p><strong>Compartilhe suas descobertas</strong> conosco e ajude a <strong>comunidade</strong> a avançar no campo da <strong>IA multimodal</strong>!</p><h2>Agradecimentos especiais</h2><p>Gostaria de agradecer a Adrian Cole por sua valiosa contribuição e revisão durante o processo de definição da arquitetura de implantação deste código.</p><h2>Referências</h2><ul><li><p><a href="https://www.elastic.co/pt/search-labs/blog/multimodal-image-retrieval-with-roboflow">Construir um sistema de recuperação de imagens multimodal usando pesquisa KNN e incorporações CLIP</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/tutorials/search-tutorial/vector-search/nearest-neighbor-search">Pesquisa de k-vizinho mais próximo (kNN)</a></p></li><li><p><a href="https://pytorch.org/docs/stable/tensors.html">Documentação oficial do PyTorch sobre tensores</a></p></li><li><p><a href="https://imagebind.metademolab.com/">ImageBind: uma nova maneira de 'conectar' a IA através dos sentidos</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/building-multimodal-rag-system</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Alex Salgado]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt75ee2f922dacb7a8/6a17ef067b54f9775a8b39a3/47635eb4dadb8481854862668231eaa3a005ebee-1600x900.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 11 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Usando Ollama com a API de Inferência]]></title>
    <description><![CDATA[Aprenda como integrar o Ollama com o Elasticsearch usando a API de Inferência.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, aprenderemos como conectar modelos locais ao modelo de inferência do Elasticsearch usando o Ollama e, em seguida, fazer perguntas aos seus documentos usando o Playground.</p><p>O Elasticsearch permite que os usuários se conectem a LLMs usando a Open <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/inference-apis.html">Inference API</a>, oferecendo suporte a provedores como Amazon Bedrock, Cohere, Google AI, Azure AI Studio, HuggingFace - como serviço, entre outros.</p><p><a href="https://ollama.com">Ollama</a> é uma ferramenta que permite baixar e executar modelos LLM usando sua própria infraestrutura (sua máquina/servidor local). <a href="https://ollama.com/library">Aqui</a> você encontra uma lista dos modelos disponíveis que são compatíveis com o Ollama.</p><p>O Ollama é uma ótima opção se você deseja hospedar e testar diferentes modelos de código aberto sem ter que se preocupar com as diferentes maneiras como cada um dos modelos pode ter que ser configurado ou sobre como criar uma API para acessar as funções do modelo, pois o Ollama cuida de tudo.</p><p>Como a API Ollama é compatível com a API OpenAI, podemos integrar facilmente o modelo de inferência e criar um aplicativo RAG usando o Playground.</p><h2>Pré-requisitos</h2><ol><li><p>Elasticsearch 8.17</p></li><li><p>Kibana 8.17</p></li><li><p>Python</p></li></ol><h2>Etapas</h2><ol><li><p><a href="https://www.elastic.co/pt/search-labs/blog/ollama-with-inference-api#setting-up-ollama-llm-server">Configurando o servidor Ollama LLM</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/blog/ollama-with-inference-api#creating-mappings">Criando mapeamentos</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/blog/ollama-with-inference-api#indexing-data">Indexação de dados</a></p></li><li><p><a href="https://www.elastic.co/pt/search-labs/blog/ollama-with-inference-api#asking-questions-using-playground">Fazendo perguntas usando o Playground</a></p></li></ol><h2>Configurando o servidor Ollama LLM</h2><p>Vamos configurar um servidor LLM para conectá-lo à nossa instância do Playground usando o Ollama. Precisaremos:</p><ul><li><p>Baixe e execute o Ollama.</p></li><li><p>Use o ngrok para acessar seu servidor web local que hospeda o Ollama pela internet</p></li></ul><h3>Baixe e execute o Ollama</h3><p>Para usar o Ollama, primeiro precisamos <a href="https://ollama.com/download">baixá-lo</a>. O Ollama oferece suporte para Linux, Windows e macOS, então basta baixar a versão do Ollama compatível com seu sistema operacional <a href="https://ollama.com/download">aqui.</a> Depois que o Ollama estiver instalado, podemos escolher um modelo desta <a href="https://ollama.com/library">lista</a> de LLMs suportados. Neste exemplo, usaremos o modelo <a href="https://ollama.com/library/llama3.2">llama3.2</a>, um modelo multilíngue geral. No processo de configuração, você habilitará a ferramenta de linha de comando do Ollama. Depois que o download for concluído, você pode executar a seguinte linha:</p>ollama pull llama3.2<p>O que produzirá:</p>pulling manifest
pulling dde5aa3fc5ff... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 2.0 GB
pulling 966de95ca8a6... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 1.4 KB
pulling fcc5a6bec9da... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 7.7 KB
pulling a70ff7e570d9... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏ 6.0 KB
pulling 56bb8bd477a5... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏   96 B
pulling 34bb5ab01051... 100% ▕█████████████████████████████████████████████████████████████████████████████████████████▏  561 B
verifying sha256 digest
writing manifest
success<p>Uma vez instalado, você pode testá-lo com este comando:</p>ollama run llama3.2<p>Vamos fazer uma pergunta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc12f240920e897f5/6a17f39425daab32a508a367/ad1eff81c1b04d2a747c3afd0ecbc215e5bd96fd-800x501.gif" alt="Execute Ollama e faça uma pergunta" /><p>Com o modelo em execução, o Ollama habilita uma API que seria executada por padrão na porta "11434". Vamos fazer uma requisição para essa API, seguindo a <a href="https://github.com/ollama/ollama/blob/main/docs/api.md">documentação oficial</a>:</p>curl http://localhost:11434/api/generate -d '{                                          
  "model": "llama3.2",               
  "prompt": "What is the capital of France?"
}' <p>Esta é a resposta que obtivemos:</p>{"model":"llama3.2","created_at":"2024-11-28T21:48:42.152817532Z","response":"The","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.251884485Z","response":" capital","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.347365913Z","response":" of","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.446837322Z","response":" France","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.542367394Z","response":" is","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.644580384Z","response":" Paris","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.739865362Z","response":".","done":false}
{"model":"llama3.2","created_at":"2024-11-28T21:48:42.834347518Z","response":"","done":true,"done_reason":"stop","context":[128006,9125,128007,271,38766,1303,33025,2696,25,6790,220,2366,18,271,128009,128006,882,128007,271,3923,374,279,6864,315,9822,30,128009,128006,78191,128007,271,791,6864,315,9822,374,12366,13],"total_duration":6948567145,"load_duration":4386106503,"prompt_eval_count":32,"prompt_eval_duration":1872000000,"eval_count":8,"eval_duration":684000000}<p><em>Observe que a resposta específica para esse ponto de extremidade é um streaming.</em></p><h3>Exponha o endpoint à internet usando o ngrok</h3><p>Como nosso endpoint funciona em um ambiente local, ele não pode ser acessado de outro ponto, como nossa instância do Elastic Cloud, pela Internet. <a href="https://ngrok.com">O ngrok</a> nos permite expor uma porta que oferece um IP público. Crie uma conta no ngrok e siga o <a href="https://dashboard.ngrok.com/get-started/setup">guia oficial de configuração</a>.</p><p>Depois que o agente ngrok for instalado e configurado, podemos expor a porta que o Ollama está usando:</p>ngrok http 11434 --host-header="localhost:11434"<p><em>Observação: o cabeçalho </em><em><code>--host-header="localhost:11434"</code></em><em> garante que o cabeçalho "Host" nas solicitações corresponda a "localhost:11434"</em></p><p>Executar este comando retornará um link público que funcionará enquanto o ngrok e o servidor Ollama forem executados localmente.</p>Session Status                online                                                                                                                                                                              
Account                       xxxx@yourEmailProvider.com (Plan: Free)                                                                                                                                             
Version                       3.18.4                                                                                                                                                                              
Region                        United States (us)                                                                                                                                                                  
Latency                       561ms                                                                                                                                                                               
Web Interface                 http://127.0.0.1:4040                                                                                                                                                               
Forwarding                    https://your-ngrok-url.ngrok-free.app -&gt; http://localhost:11434                                                                                                                   


Connections                   ttl     opn     rt1     rt5     p50     p90                                                                                                                                         
                              0       0       0.00    0.00    0.00    0.00                                                ```<p>Em "Encaminhamento" podemos ver que o ngrok gerou uma URL. Guarde para mais tarde.</p><p>Vamos tentar fazer uma solicitação HTTP para o endpoint novamente, agora usando a URL gerada pelo ngrok:</p>curl https://your-ngrok-endpoint.ngrok-free.app/api/generate -d '{                                          
  "model": "llama3.2",               
  "prompt": "What is the capital of France?"
}'<p>A resposta deve ser semelhante à anterior.</p><h2>Criando mapeamentos</h2><h3>Ponto final ELSER</h3><p>Neste exemplo, <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/put-inference-api.html">criaremos um ponto de extremidade de inferência usando a API de inferência do Elasticsearch</a>. Além disso, usaremos <a href="https://www.elastic.co/pt/guide/en/machine-learning/current/ml-nlp-elser.html">o ELSER</a> para gerar os embeddings.</p>PUT _inference/sparse_embedding/medicines-inference
{
  "service": "elasticsearch",
  "service_settings": {
    "num_allocations": 1,
    "num_threads": 1,
    "model_id": ".elser_model_2_linux-x86_64"
  }
}<p>Para este exemplo, vamos imaginar que você tem uma farmácia que vende dois tipos de medicamentos:</p><ul><li><p>Medicamentos que exigem receita médica.</p></li><li><p>Medicamentos que NÃO exigem receita médica.</p></li></ul><p>Essas informações seriam incluídas no campo de descrição de cada medicamento.</p><p>O LLM deve interpretar esse campo, então estes são os mapeamentos de dados que usaremos:</p>PUT medicines
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "copy_to": "semantic_field"
      },
      "semantic_field": {
        "type": "semantic_text",
        "inference_id": "medicines-inference"
      },
      "text_description": {
        "type": "text",
        "copy_to": "semantic_field"
      }
    }
  }
}<p>O campo <code>text_description</code> armazenará o texto simples das descrições, enquanto <code>semantic_field</code>, que é um tipo de campo <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/semantic-text.html">semantic_text</a> , armazenará os embeddings gerados pelo ELSER.</p><p>A propriedade <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/copy-to.html">copy_to</a> copiará o conteúdo dos campos name e <code>text_description</code> para o campo semântico para que os embeddings para esses campos sejam gerados.</p><h2>Indexação de dados</h2><p>Agora, vamos indexar os dados usando a <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/docs-bulk.html">API _bulk</a>.</p>POST _bulk
{"index":{"_index":"medicines"}}
{"id":1,"name":"Paracetamol","text_description":"An analgesic and antipyretic that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":2,"name":"Ibuprofen","text_description":"A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":3,"name":"Amoxicillin","text_description":"An antibiotic that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":4,"name":"Lorazepam","text_description":"An anxiolytic medication that strictly requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":5,"name":"Omeprazole","text_description":"A medication for stomach acidity that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":6,"name":"Insulin","text_description":"A hormone used in diabetes treatment that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":7,"name":"Cold Medicine","text_description":"A compound formula to relieve flu symptoms available WITHOUT a prescription."}
{"index":{"_index":"medicines"}}
{"id":8,"name":"Clonazepam","text_description":"An antiepileptic medication that requires a prescription."}
{"index":{"_index":"medicines"}}
{"id":9,"name":"Vitamin C","text_description":"A dietary supplement that does NOT require a prescription."}
{"index":{"_index":"medicines"}}
{"id":10,"name":"Metformin","text_description":"A medication used for type 2 diabetes that requires a prescription."}<p>Resposta.</p>{
   "errors": false,
   "took": 34732020848,
   "items": [
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "mYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 0,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "mooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 1,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "m4oeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 2,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nIoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 3,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 4,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "nooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 5,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "n4oeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 6,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oIoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 7,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oYoeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 8,
     	"_primary_term": 1,
     	"status": 201
   	}
 	},
 	{
   	"index": {
     	"_index": "medicines",
     	"_id": "oooeMpQBF7lnCNFTfdn2",
     	"_version": 1,
     	"result": "created",
     	"_shards": {
       	"total": 2,
       	"successful": 2,
       	"failed": 0
     	},
     	"_seq_no": 9,
     	"_primary_term": 1,
     	"status": 201
   	}
 	}
   ]
 }<h2>Fazendo perguntas usando o Playground</h2><p><a href="https://www.elastic.co/pt/guide/en/kibana/current/playground.html">Playground</a> é uma ferramenta do Kibana que permite criar rapidamente um sistema RAG usando índices do Elasticsearch e um provedor LLM. Você pode ler este <a href="https://www.elastic.co/pt/search-labs/blog/playground-connectors-data-chat">artigo</a> para saber mais sobre isso.</p><h3>Conectando o LLM local ao Playground</h3><p>Primeiro, precisamos criar um conector que use a URL pública que acabamos de criar. No Kibana, vá em <strong>Pesquisar&gt;Playground</strong> e depois clique em "Conectar a um LLM".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt22148eabfabf6d3f/6a17f3963e9e459f97ba15c6/1854f0808f8150e359fe62ba5d901d32a88d477c-1600x867.png" alt="Conectando o LLM local ao Playground para Ollama" /><p>Esta ação revelará um menu no lado esquerdo da interface do Kibana. Lá, clique em "OpenAI".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e28194d9012f141/6a17f39725daab500a08a36b/c83d3c4d7035a518124ad7d22b38764db57b6800-933x1007.png" alt="Selecione um conector: Open AI Ollama" /><p>Agora podemos começar a configurar o conector OpenAI.</p><p>Vá para "Configurações do conector" e, para o provedor OpenAI, selecione "Outro (Serviço compatível com OpenAI)":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9984dce6f78a7c08/6a17f3990b0bed0b7add36c4/ecfcdc4b575c309bd55b4e61ca0ddb348aa84f64-917x268.png" alt="Definir a configuração do conector para usar o Ollama com a API de Inferência" /><p>Agora, vamos configurar os outros campos. Para este exemplo, vamos nomear nosso modelo como "medicines-llm". No campo URL, use o gerado pelo ngrok (/v1/chat/completions). No campo "Modelo padrão", selecione "llama3.2". Não usaremos uma chave de API, então digite qualquer texto aleatório para prosseguir:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a24b93a39d380fb/6a17f39b96142a15c7eb1c3c/5d3b5027c8096cbe49fb740d70aa24e849611a9d-916x688.png" alt="Adicionar configurações" /><p>Clique em "Salvar" e adicione os medicamentos de índice clicando em "Adicionar fontes de dados":</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f107a54d5be25f9/6a17f39d4b055deb9d432338/525113da59e902c8235f62bde8fb62371a63e11b-1579x753.png" alt="Adicione fontes de dados para fazer perguntas aos seus documentos usando o Playground" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt03fb36fe05dcdbf5/6a17f39ebe608602f40048be/96138de0bbe2c2ac619f64889d3487df62739ca4-466x805.png" alt="Adicionar dados de consulta" /><p>Ótimo! Agora temos acesso ao Playground usando o LLM que estamos executando localmente como mecanismo RAG.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb1b27580107259b3/6a17f3a096142abefceb1c40/cfb48b33c70f4534ab77eb01f58008237f65e6f4-1600x851.png" alt="Selecione as configurações do modelo no Playground" /><p>Antes de testar, vamos adicionar instruções mais específicas ao agente e aumentar o número de documentos enviados ao modelo para 10, para que a resposta tenha o maior número possível de documentos disponíveis. O campo de contexto será <code>semantic_field</code>, que inclui o nome e a descrição dos medicamentos, graças à propriedade copy_to.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4fbc97dc87c6fc62/6a17f3a1e8fbce052c3a1aa0/0c57c9c0e1a0e7b58fffdd3ef81d67d41e2990c4-580x806.png" alt="Configurações Moel no Elastic Playground" /><p>Agora vamos fazer a pergunta: <em><strong>Posso comprar Clonazepam sem receita?</strong></em> e veja o que acontece:</p><p>Como esperado, obtivemos a resposta correta.</p><h3>Próximas etapas</h3><p>O próximo passo é criar seu próprio aplicativo! O Playground fornece um script de código em Python que você pode executar em sua máquina e personalizá-lo para atender às suas necessidades. Por exemplo, colocando-o atrás de um servidor <a href="https://fastapi.tiangolo.com/">FastAPI</a> para criar um chatbot de medicamentos de controle de qualidade consumido pela sua interface de usuário.</p><p>Você pode encontrar esse código clicando no botão <em><strong>Exibir código</strong></em> na seção superior direita do Playground:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42fe193b8aa08830/6a17f3a33e9e4569e8ba15ca/816bfd0e5f936ad65dbe719d5df10714e550a40b-380x121.png" alt="Botão Ver código" /><p>E você usa os <em><strong>Endpoints e as chaves de API</strong></em> para gerar a variável de ambiente <code>ES_API_KEY</code> necessária no código.</p><p>Para este exemplo específico, o código é o seguinte:</p>## Install the required packages
## pip install -qU elasticsearch openai
import os
from elasticsearch import Elasticsearch
from openai import OpenAI
es_client = Elasticsearch(
    "https://your-deployment.us-central1.gcp.cloud.es.io:443",
    api_key=os.environ["ES_API_KEY"]
)
openai_client = OpenAI(
    api_key=os.environ["OPENAI_API_KEY"],
)
index_source_fields = {
    "medicines": [
        "semantic_field"
    ]
}
def get_elasticsearch_results():
    es_query = {
        "retriever": {
            "standard": {
                "query": {
                    "nested": {
                        "path": "semantic_field.inference.chunks",
                        "query": {
                            "sparse_vector": {
                                "inference_id": "medicines-inference",
                                "field": "semantic_field.inference.chunks.embeddings",
                                "query": query
                            }
                        },
                        "inner_hits": {
                            "size": 2,
                            "name": "medicines.semantic_field",
                            "_source": [
                                "semantic_field.inference.chunks.text"
                            ]
                        }
                    }
                }
            }
        },
        "size": 3
    }
    result = es_client.search(index="medicines", body=es_query)
    return result["hits"]["hits"]
def create_openai_prompt(results):
    context = ""
    for hit in results:
        inner_hit_path = f"{hit['_index']}.{index_source_fields.get(hit['_index'])[0]}"
        ## For semantic_text matches, we need to extract the text from the inner_hits
        if 'inner_hits' in hit and inner_hit_path in hit['inner_hits']:
            context += '\n --- \n'.join(inner_hit['_source']['text'] for inner_hit in hit['inner_hits'][inner_hit_path]['hits']['hits'])
        else:
            source_field = index_source_fields.get(hit["_index"])[0]
            hit_context = hit["_source"][source_field]
            context += f"{hit_context}\n"
    prompt = f"""
  Instructions:
  - You are an assistant specializing in answering questions about the sale of medicines.
  - Answer questions truthfully and factually using only the context presented.
  - If you don't know the answer, just say that you don't know, don't make up an answer.
  - You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
  - Use markdown format for code examples.
  - You are correct, factual, precise, and reliable.
  Context:
  {context}
  """
    return prompt
def generate_openai_completion(user_prompt, question):
    response = openai_client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": user_prompt},
            {"role": "user", "content": question},
        ]
    )
    return response.choices[0].message.content
if __name__ == "__main__":
    question = "my question"
    elasticsearch_results = get_elasticsearch_results()
    context_prompt = create_openai_prompt(elasticsearch_results)
    openai_completion = generate_openai_completion(context_prompt, question)
    print(openai_completion)<p>Para que funcione com o Ollama, você precisa alterar o cliente OpenAI para se conectar ao servidor Ollama em vez do servidor OpenAI. Você pode encontrar a lista completa de exemplos do OpenAI e endpoints compatíveis aqui.</p>openai_client = OpenAI(
    # you can use http://localhost:11434/v1/ if running this code locally.
    base_url='https://your-ngrok-url.ngrok-free.app/v1/',
    # required but ignored
    api_key='ollama',
)<p>E também altere o modelo para llama3.2 ao chamar o método de conclusão:</p>def generate_openai_completion(user_prompt, question):
    response = openai_client.chat.completions.create(
        model="llama3.2",
        messages=[
            {"role": "system", "content": user_prompt},
            {"role": "user", "content": question},
        ]
    )
    return response.choices[0].message.content<p>Vamos adicionar nossa pergunta: <em><strong>Posso comprar Clonazepam sem receita? </strong></em>Para a consulta do Elasticsearch:</p>def get_elasticsearch_results():
    es_query = {
        "retriever": {
            "standard": {
                "query": {
                    "nested": {
                        "path": "semantic_field.inference.chunks",
                        "query": {
                            "sparse_vector": {
                                "inference_id": "medicines-inference",
                                "field": "semantic_field.inference.chunks.embeddings",
                                "query": "Can I buy Clonazepam without a prescription?"
                            }
                        },
                        "inner_hits": {
                            "size": 2,
                            "name": "medicines.semantic_field",
                            "_source": [
                                "semantic_field.inference.chunks.text"
                            ]
                        }
                    }
                }
            }
        },
        "size": 3
    }
    result = es_client.search(index="medicines", body=es_query)
    return result["hits"]["hits"]<p>E também para a chamada de conclusão com algumas impressões, para que possamos confirmar que estamos enviando os resultados do Elasticsearch como parte do contexto da pergunta:</p>if __name__ == "__main__":
    question = "Can I buy Clonazepam without a prescription?"
    elasticsearch_results = get_elasticsearch_results()
    context_prompt = create_openai_prompt(elasticsearch_results)
    print("========== Context Prompt START ==========")
    print(context_prompt)
    print("========== Context Prompt END ==========")
    print("========== Ollama Completion START ==========")
    openai_completion = generate_openai_completion(context_prompt, question)
    print(openai_completion)
    print("========== Ollama Completion END ==========")<p>Agora vamos executar o comando</p><p><code>pip install -qU elasticsearch openai</code></p><p><code>python main.py</code></p><p>Você deverá ver algo assim:</p>========== Context Prompt START ==========
  Instructions:
  - You are an assistant specializing in answering questions about the sale of medicines.
  - Answer questions truthfully and factually using only the context presented.
  - If you don't know the answer, just say that you don't know, don't make up an answer.
  - You must always cite the document where the answer was extracted using inline academic citation style [], using the position.
  - Use markdown format for code examples.
  - You are correct, factual, precise, and reliable.
  Context:
  Clonazepam
 ---
An antiepileptic medication that requires a prescription.A nonsteroidal anti-inflammatory drug (NSAID) available WITHOUT a prescription.
 ---
IbuprofenAn anxiolytic medication that strictly requires a prescription.
 ---
Lorazepam


========== Context Prompt END ==========
========== Ollama Completion START ==========
No, you cannot buy Clonazepam over-the-counter (OTC) without a prescription [1]. It is classified as a controlled substance in the United States due to its potential for dependence and abuse. Therefore, it can only be obtained from a licensed healthcare provider who will issue a prescription for this medication.
========== Ollama Completion END ==========<h2>Conclusão</h2><p>Neste artigo, podemos ver o poder e a versatilidade de ferramentas como o Ollama quando as usamos em conjunto com a API de inferência do Elasticsearch e o Playground.</p><p>Após alguns passos simples, tínhamos um aplicativo RAG funcional com um chat que usava um LLM em execução em nossa própria infraestrutura a custo zero. Isso também nos permite ter mais controle sobre recursos e informações confidenciais, além de nos dar acesso a uma variedade de modelos para diferentes tarefas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ollama-with-inference-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ollama-with-inference-api</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd9c8eb0fc946920e/6a17f3a46864a4b2fbb688f0/399b9ef527be633845fb6505b68132cc03bc9e09-1150x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 14 Feb 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Testando o DeepSeek R1 localmente para RAG com Ollama e Kibana]]></title>
    <description><![CDATA[Saiba como executar uma instância local do DeepSeek e conectá-la a partir do Kibana.]]></description>
    <content:encoded><![CDATA[<p>Todos estão comentando sobre o DeepSeek R1, o novo grande modelo de linguagem do fundo de hedge chinês High-Flyer. As notícias estão repletas de especulações sobre o que isso significa para a indústria agora que eles introduziram um LLM capaz de raciocínio em cadeia de pensamento com pesos abertos. Para os curiosos em experimentar este novo modelo com RAG e todas as capacidades do banco de dados vetorial do Elasticsearch, aqui está um breve tutorial para começar a usar o DeepSeek R1 com inferência local. Ao longo do caminho, usaremos o recurso Playground da Elastic e até descobrir algumas boas e más propriedades do Deepseek R1 para RAG.</p><p>Aqui está um diagrama do que configuraremos neste tutorial:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3214cc505e3d4d06/6a17df98dbb4ff12bafb55da/8aafec9011e986cd85b10958544a4d77be81e518-739x419.png" alt="Configuração do Deepseek com Elasticsearch e Ollama" /><h2>Configurando a inferência local com Ollama</h2><p><a href="https://ollama.com/">Ollama</a> é uma excelente maneira de testar rapidamente um conjunto selecionado de modelos open source para inferência local e é uma ferramenta popular entre desenvolvedores de IA.</p><h3>Executando Ollama bare metal</h3><p>Uma <a href="https://github.com/ollama/ollama/tree/main?tab=readme-ov-file#ollama">instalação local</a> no Mac, Linux ou Windows é a maneira mais fácil de aproveitar qualquer capacidade de GPU local que você possa ter, principalmente para aqueles com chips Apple da série M. Depois de ter o Ollama instalado, você pode baixar e executar o DeepSeek R1 com o seguinte comando.</p><p>Talvez seja bom ajustar o tamanho do parâmetro para que ele se adeque ao seu hardware. Os tamanhos disponíveis podem ser encontrados <a href="https://ollama.com/library/deepseek-r1">aqui</a>.</p>ollama run deepseek-r1:7b<p>Você pode conversar com o modelo no terminal, mas o modelo permanece em execução quando você pressiona Ctrl+d para sair do comando ou digita "/bye". Para ver o modelo ainda em execução, digite:</p>ollama ps<h3>Executando Ollama em um container</h3><p>Como alternativa, a maneira mais rápida de executar o Ollama é utilizando um mecanismo de container como o Docker. Usar a GPU da sua máquina local nem sempre é tão simples, dependendo do seu ambiente, mas obter uma configuração de teste rápida não é difícil, desde que o container tenha a RAM e o armazenamento adequados aos modelos de vários GB.</p><p>Colocar o Ollama em execução no Docker é tão fácil quanto executar:</p>mkdir ollama_deepseek
cd ollama_deepseek
mkdir ollama
docker run -d -v ./ollama:/root/.ollama -p 11434:11434 \
--name ollama ollama/ollama
<p>Isso cria um diretório chamado "ollama" no diretório atual e o monta dentro do container para armazenar a configuração do Ollama e também os modelos. Dependendo do número de parâmetros usados, eles podem variar de alguns GBs a dezenas de GBs. Portanto, certifique-se de escolher um volume com espaço livre suficiente.</p><p>Observação: se você tiver uma GPU Nvidia em sua máquina, certifique-se de instalar o <a href="https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installation">Nvidia container toolkit</a> e adicionar "--gpus=all" ao comando docker executar acima.</p><p>Assim que o container do Ollama estiver em execução na sua máquina, você pode puxar um modelo como o deepseek-r1 com:</p>docker exec -it ollama ollama pull deepseek-r1:7b<p>Semelhante à abordagem de máquinas físicas, talvez você queira ajustar o tamanho do parâmetro para algo que se adeque ao seu hardware. Os tamanhos disponíveis podem ser encontrados em <a href="https://ollama.com/library/deepseek-r1">https://ollama.com/library/deepseek-r1</a>.</p><p>Assim que o modelo terminar de ser puxado, você pode digitar “/bye” para sair das instruções. Para confirmar que o modelo ainda está em execução:</p>docker exec -it ollama ollama ps<h2>Testando nossa inferência local com um curl</h2><p>Para testar a inferência local com curl, você pode executar o seguinte comando. Estamos usando stream:false para que possamos ler facilmente a resposta narrativa JSON:</p>curl http://localhost:11434/api/generate -d '{
  "model": "deepseek-r1:7b",
  "stream": false,
  "prompt":"Why is Elastic so cool?"
}'<h2>Testando o Ollama “compatível com OpenAI” e instruções RAG</h2><p>Convenientemente, o Ollama também oferece um endpoint REST que imita o comportamento do OpenAI para compatibilidade com uma ampla gama de ferramentas, incluindo o Kibana.</p>curl http://localhost:11434/v1/chat/completions -d '{
  "model": "deepseek-r1:7b",
  "stream": false,
  "messages": [
    { 
      "role": "system", 
      "content": "You are a helpful AI Assistant that uses the following context to answer questions only use the following context. \n\nContext:  The color of the sky today is purple. "},
    { "role": "user", 
      "content": "What does the sky look like today?" 
    }
  ]
}'<p>Testar essas instruções mais complexas resulta em um conteúdo que possui uma seção &lt;think&gt;, onde o modelo foi treinado para raciocinar sobre o problema.</p>&lt;think&gt; 
Okay, so I need to figure out what the user is asking for here. They provided a context where the sky is described as purple today and then asked about how the sky looks. At first glance, it seems straightforward—maybe they just want confirmation or more details on why the sky is that color.
Wait, but maybe there's something deeper. Purple skies aren't something I encounter every day. It usually happens at certain times of the year, like during sunrise or sunset with the sun setting in pink or orange. Could this be a hint about the time of day? Or perhaps it's just an unusual natural phenomenon? 
I should consider if \"purple\" is a typo. Maybe they meant something else like blue or gray. But since they specifically said purple, I'll go with that. Purple skies can happen when there are atmospheric conditions that scatter light differently, maybe due to pollution or cloud cover affecting the sunset.

So, putting it all together, the user might be looking for an explanation of why today's sky is purple and what that implies about the weather or time of day. Alternatively, they could just want a simple statement confirming that the sky looks purple today.
&lt;/think&gt;

The color of the sky today is described as purple. This unusual shade can occur due to atmospheric conditions affecting light scattering, such as during sunrise/sunset with pollution or cloud cover influencing the sunset's hues.<h2>Conectando Ollama ao Kibana</h2><p>Uma excelente maneira de usar o Elasticsearch é o script de desenvolvimento "<a href="https://github.com/elastic/start-local?tab=readme-ov-file#-try-elasticsearch-and-kibana-locally">start-local</a>".</p><p>Certifique-se de que seu Kibana e Elasticsearch consigam acessar seu Ollama na rede. Se você estiver usando uma configuração de container local do Elastic stack, isso pode significar substituir "localhost" por "host.docker.internal". ou “host.containers.internal” para obter um caminho de rede para a máquina hospedada.</p><p>No Kibana, navegue até Stack Management &gt; Alerts and Insights &gt; Connectors.</p><h3>O que fazer se você vir que este é um aviso comum de configuração</h3><p>Você precisará garantir que o xpack.encryptedSavedObjects.encryptionKey <a href="https://www.elastic.co/guide/en/kibana/current/xpack-security-secure-saved-objects.html">esteja configurado corretamente</a>. Esta é uma etapa comumente esquecida ao executar uma instalação local do Docker do Kibana, então listarei as etapas para corrigir na sintaxe do Docker.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta4f7b7be2e04afae/6a17df9a1d1b8391e293e393/b70b4b810bcac1d1599b07da90a98c5c744a38de-497x223.png" alt="" /><p>Certifique-se de persistir seu diretório kibana/config para que as alterações sejam salvas quando o container for encerrado. Meus volumes de container do Kibana se parecem com isso em docker-compose.yml:</p>services:
  kibana:
...
   volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
      - kibanaconfig:/usr/share/kibana/config
...
volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  kibanadata:
    driver: local
  kibanaconfig:
    driver: local<p>Agora você pode criar o repositório de chaves e inserir um valor para que as chaves do Connector não sejam armazenadas em texto simples.</p>## generate some new keys for me and print them to the terminal
docker exec -it kibana_1 bin/kibana-encryption-keys generate

## create a new keystrore
docker exec -it kibana_1 bin/kibana-keystore create
docker exec -it kibana_1 bin/kibana-keystore add xpack.encryptedSavedObjects.encryptionKey

## You'll be prompted to paste in a value<p>Reinicie completamente todo o cluster para que as alterações entrem em vigor.</p><h3>Criando o Conector</h3><p>Na tela de configuração do conector (no Kibana, navegue até Stack Management &gt; Alerts and Insights &gt; Connectors), crie um conector e selecione o tipo "OpenAI".</p><p>Configure o conector com as seguintes definições</p><ul><li><p>Nome do conector: Deepseek (Ollama)</p></li><li><p>Selecione um provedor OpenAI: outro (Serviço Compatível com OpenAI)</p></li><li><p>URL: <a href="http://localhost:11434/v1/chat/completions">http://localhost:11434/v1/chat/completions</a></p><ul><li><p>Ajuste o caminho correto para o seu Ollama. Lembre-se de substituir host.docker.internal ou equivalente se estiver chamando de dentro de um container.</p></li></ul></li><li><p>Modelo padrão: deepseek-r1:7b</p></li><li><p>Chave de API: invente algo, uma entrada é necessária, mas o valor não importa</p></li></ul><p>Observe que testar um conector personalizado para Ollama na configuração do conector está atualmente com problemas na versão 8.17, mas foi corrigido na próxima versão 8.18 do Kibana.</p><p>Nosso conector é assim:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774e0793eb110f9d/6a17df9c445de981014d004d/4ce214aa953b4090ed112fbde40b01c01fb8f5c7-786x836.png" alt="" /><h2>Inserindo dados de vetores incorporados no Elasticsearch</h2><p>Se você já estiver familiarizado com o Playground e tiver os dados configurados, pode pular para a etapa do Playground abaixo. Mas, se precisar de alguns dados de teste rápidos, precisaremos garantir que nossas API de inferência estejam configuradas. A partir da versão 8.17, as alocações de machine learning são dinâmicas, portanto, para baixar e ativar o vetor denso multilíngue e5, precisaremos apenas executar o seguinte nas ferramentas de desenvolvimento do Kibana.</p>GET /_inference


POST /_inference/text_embedding/.multilingual-e5-small-elasticsearch
{
   "input": "are internet memes about deepseek sound investment advice?"
}<p>Se você ainda não o fez, isso acionará o download do modelo e5 dos repositórios de modelos da Elastic.</p><p>Em seguida, vamos carregar um livro de domínio público como nosso contexto RAG. Aqui está um lugar para baixar “Alice’s Adventures in Wonderland” do Projeto Gutenberg: <a href="https://www.gutenberg.org/cache/epub/11/pg11.txt">link</a>. Salve como arquivo .txt.</p><p>Navegue até Elasticsearch &gt; Home &gt; Carregar um arquivo</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt45c594487844ecb4/6a17df9dfaa9137edb93c786/649042271f34a5e66789b17c39bfe95971c7f4ce-1360x629.png" alt="" /><p>Selecione ou arraste e solte seu arquivo de texto e clique no botão "Importar".</p><p>Na tela “Import data” (Importar dados), selecione a guia “Advanced” (Avançado) e defina o nome do índice como “book_alice”.</p><p>Selecione a opção “Add additional campo” (Adicionar campo adicional), que fica logo abaixo de “Automatically created campos” (Campos criados automaticamente). Selecione “Adicionar campo de texto semântico” e altere o endpoint de inferência para “.multilingual-e5-small-elasticsearch”. Selecione Add e, em seguida, Import.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d9c22ccdaee8590/6a17df9f3e03d731f94f2b8e/e58d5c9a2d406d8e62eb96cab9ac98ca89414346-507x602.png" alt="" /><p></p><p>Quando o carregamento e a inferência estiverem concluídos, estaremos prontos para ir para o Playground.</p><h2>Testando o RAG no Playground</h2><p>Navegue até Elasticsearch &gt; Playground no Kibana.</p><p>Na tela do playground, você verá uma marca de visto verde e “LLM Connected” para indicar que um conector existe. Este é o conector Ollama que acabamos de criar acima. Um guia mais longo para o Playground pode ser encontrado <a href="https://www.elastic.co/guide/en/kibana/current/playground.html">aqui</a>.</p><p>Clique na opção azul Add data sources (Adicionar fontes de dados) e selecione o índice book_alice que já criamos ou outro índice que você já tenha configurado e que utilize API de inferência para embeddings.</p><p>O Deepseek é um modelo de cadeia de pensamento com características fortes de alinhamento. Isso é tanto bom quanto ruim do ponto de vista do RAG. O treinamento em cadeia de pensamento pode ajudar o Deepseek a racionalizar afirmações aparentemente contraditórias nas citações, mas o forte alinhamento com o conhecimento do treinamento pode fazer com que ele prefira sua própria versão dos fatos mundiais em vez da nossa base contextual. Embora bem-intencionado, esse forte alinhamento é conhecido por dificultar a instrução dos LLMs ao discutir tópicos em que nosso conhecimento particular é limitado ou não está bem representado no conjunto de dados de treinamento.</p><p>Na nossa configuração do Playground, inserimos as seguintes instruções do sistema: "Você é um assistente para tarefas de resposta a perguntas usando passagens de texto relevantes do livro Alice no País das Maravilhas" e aceitamos os outros padrões.</p><p>À pergunta “Quem estava na festa do chá?”, obtemos a resposta: “Resposta: A Lebre de Março, o Chapeleiro e o Arganaz estavam na festa do chá. [Citação: posições 1 e 2]", o que está correto.
</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ce6a0facd972fdd/6a17dfa03e03d79aaa4f2b92/e8af3ff93a72e1f02de8e73f6c2606cbc19970e5-1296x813.png" alt="" /><p>Podemos ver nas tags &lt;think&gt; que o Deepseek definitivamente ponderou o conteúdo das citações para responder às perguntas.</p><h2>Testando as limitações de alinhamento</h2><p>Vamos criar um caso intelectualmente desafiador para o Deepseek como um teste. Criaremos um índice de teorias da conspiração que os dados de treinamento do Deepseek sabem que não são verdadeiras.</p><p>Nas Dev Tools do Kibana, vamos criar o seguinte índice e dados:</p>PUT /classic_conspiracies
{
   "mappings": {
       "properties": {
           "content": {
               "type": "text",
               "copy_to": "content_semantic"
           },
           "content_semantic": {
               "type": "semantic_text",
               "inference_id": ".multilingual-e5-small-elasticsearch"
           }
       }
   }
}




POST /classic_conspiracies/_doc/1
{
   "content": "birds aren't real, the government replaced them with drones a long time ago"
}
POST /classic_conspiracies/_doc/2
{
   "content": "tinfoil hats are necessary to prevent our brains from being read"
}
POST /classic_conspiracies/_doc/3
{
   "content": "ancient aliens influenced early human civilizations, this explains why things made out of stone are marginally similar on different continents"
}<p>
Essas teorias da conspiração serão nosso fundamento para o LLM. Apesar de inserir instruções agressivas no sistema, o Deepseek não aceitará nossa versão dos fatos. Se estivéssemos em uma situação em que soubéssemos que nossos dados privados eram mais confiáveis, fundamentados ou alinhados às necessidades de nossa organização, isso não seria aceitável:</p><p>Para a pergunta do teste "os pássaros são reais?" (explicação <a href="https://knowyourmeme.com/memes/birds-arent-real">conheça seu meme</a>) obtemos a resposta "No contexto fornecido, os pássaros não são considerados reais, mas na realidade, eles são animais reais." [Contexto: posição 1]. Este teste prova que o DeepSeek R1 é poderoso, mesmo no nível de parâmetro 7B... no entanto, pode não ser a melhor escolha para RAG, dependendo do nosso conjunto de dados.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e8dabf65ea200e1/6a17dfa2ec0f8982135a6541/67d5f6cdb97bfd3adb926cbd588c768f9d6730ae-1277x737.png" alt="" /><h2>Então, o que aprendemos?</h2><p>Em resumo:</p><ul><li><p>Executar modelos localmente em ferramentas como o Ollama é uma ótima opção para dar uma olhada no comportamento do modelo.</p></li><li><p>O DeepSeek R1 é um modelo de raciocínio, o que significa que tem vantagens e desvantagens para casos de uso como RAG.</p></li><li><p>O Playground é capaz de se conectar a frameworks de hospedagem de inferência, como Ollama, por meio de uma REST API semelhante à OpenAI, que está se tornando um padrão de fato nesta era inicial da hospedagem de IA.</p></li></ul><p>De modo geral, estamos impressionados com o quanto o RAG "air gapped" local evoluiu. As ferramentas do Elasticsearch, do Kibana e os modelos de pesos abertos disponíveis avançaram significativamente desde que escrevemos pela primeira vez sobre <a href="https://www.elastic.co/search-labs/blog/privacy-first-ai-search-langchain-elasticsearch">buscas com IA que priorizam privacidade</a>, em 2023.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/deepseek-rag-ollama-playground</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/deepseek-rag-ollama-playground</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Kibana]]></category>
    <dc:creator><![CDATA[Dave Erickson,Jakob Reiter]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2a4b2ae6bd97850b/6a17dfa4be6086558f00464c/1bd853bfdfa2710e44cc4c08dede6bd21b35c4b8-1542x860.png" length="0" type="image/png"/>
    <pubDate>Thu, 30 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Busca facetada: Use IA para aprimorar o escopo e os resultados da busca.]]></title>
    <description><![CDATA[Descubra como usar a pesquisa facetada no Elasticsearch para refinar rapidamente as opções dentro de categorias.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, exploraremos como a Inteligência Artificial (IA), especificamente utilizando modelos de linguagem avançados como o GPT-4, pode ajudar a criar facetas mais contextuais, tornando-as ainda mais relevantes e úteis para os usuários.</p><p>A busca facetada é uma ferramenta poderosa em plataformas de comércio eletrônico. Isso ajuda a organizar e refinar os resultados da pesquisa com base nas características dos itens exibidos. Embora frequentemente confundidos com filtros, os facets funcionam de maneira diferente. Os filtros são atributos fixos, definidos por informações sempre presentes no índice, como a categoria ou o formato de um produto. As facetas, por outro lado, são dinâmicas e geradas a partir dos resultados retornados pela pesquisa executada.</p><p>Imagine um catálogo de roupas: campos como "categoria" (por exemplo, camisetas, calças) ou "gênero" (por exemplo, masculino, feminino) são filtros que ajudam a refinar os resultados. As facetas, no entanto, refletem características específicas dos produtos que aparecem nos resultados, como cores comuns, tamanhos disponíveis ou materiais. Isso permite uma experiência de busca mais adaptável e contextual.</p><p>Abaixo, você encontrará uma imagem onde interagimos com uma faceta e podemos ver os resultados da pesquisa filtrados por ela.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4ce8ca7e5b62ad7/6a17f8ba6864a4534bb68979/74d2159706ab7248ebe5efddc74c882f0693db71-600x420.gif" alt="Exemplo de pesquisa facetada" /><h2>Como a IA pode aprimorar a geração de facetas</h2><p>A Inteligência Artificial é frequentemente associada à busca semântica e aos embeddings, mas e quanto às facetas? Como a IA pode ser utilizada para tornar os filtros mais úteis e específicos ao contexto de cada pesquisa?</p><p>Uma possibilidade intrigante é usar IA para criar novas categorizações que vão além das classificações tradicionais do índice. Ao analisar características específicas do conteúdo, essas novas categorias podem fornecer uma contextualização mais rica e precisa, tornando os aspectos mais relevantes e alinhados às necessidades dos usuários. Isso possibilita um refinamento mais significativo dos resultados em comparação com as categorias de documentos originais.</p><h2>Como a IA pode refinar as classificações de filmes para melhores buscas</h2><p>Vamos analisar os seguintes filmes, todos atualmente classificados no gênero Drama:</p><ul><li><p>Réquiem para um Sonho
 Resumo: As utopias induzidas por drogas de quatro pessoas de Coney Island são destruídas quando seus vícios se tornam profundos.</p></li><li><p>Beleza Americana
 Resumo: Um pai suburbano sexualmente frustrado entra em crise de meia-idade após se apaixonar pela melhor amiga de sua filha.</p></li><li><p>Good Will Hunting
 Resumo: Will Hunting, um zelador do MIT, tem um talento especial para matemática, mas precisa da ajuda de um psicólogo para encontrar um rumo na vida.</p></li></ul><p>Essa classificação por gênero não capta as diferenças sutis ou os contextos únicos de cada filme. Ao utilizar inteligência artificial para analisar sinopses e temas centrais, podemos criar novas categorias que reflitam melhor o verdadeiro contexto de cada filme. Por exemplo:</p><ul><li><p>Réquiem para um Sonho - Nova categoria: "Vício e Dependência"</p></li><li><p>Beleza Americana - Nova Categoria: "Crise da Meia-Idade"</p></li><li><p>Gênio Indomável - Nova categoria: "Luta Intelectual"</p></li></ul><p>Essas novas categorias tornam as buscas muito mais precisas, ao mesmo tempo que oferecem aos usuários filtros mais relevantes para refinar seus resultados. Essa abordagem é particularmente eficaz quando as categorias originais são muito genéricas, permitindo que os usuários encontrem exatamente o que procuram com maior facilidade.</p><h2>Criando novas categorias com GPT-4: Exemplo de busca facetada</h2><p>Neste exemplo, mostraremos como um modelo de IA pode ser usado para criar novas categorias de filmes que sejam mais precisas e alinhadas ao contexto de cada obra. Para demonstrar esse processo, utilizaremos o pipeline de simulação da Elastic juntamente com o serviço de inferência da OpenAI. Será criado um pipeline com vários processadores, incluindo o Processador de Scripts, que será responsável por criar o prompt a ser executado no Processador de Inferência, capaz de determinar as novas categorias. Os demais processadores serão utilizados para manipular os dados e os campos auxiliares gerados durante a execução do pipeline. Vale ressaltar que essa lógica também pode ser aplicada a outras ferramentas ou modelos semelhantes.</p><p>Primeiro, precisamos criar o endpoint de inferência, onde definimos o serviço como OpenAI, o token necessário para acessar o serviço e o modelo. Neste exemplo, estou usando gpt-4o-mini. Para obter mais detalhes sobre o serviço de inferência da OpenAI, clique <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">aqui</a>.</p>PUT _inference/completion/generate_topics_ia
{
    "service": "openai",
    "service_settings": {
        "api_key": "your-token",
        "model_id": "gpt-4o-mini"
    }
}<p>Com o endpoint criado, agora estamos prontos para usá-lo para criar as novas categorias. A seguir, apresentamos um fluxo de trabalho que gerencia todo o processo de manipulação de dados de documentos e geração de prompts. Explicarei detalhadamente a função de cada processador.</p><p>O primeiro processador será responsável por construir o prompt. É muito importante detalhar claramente as instruções para que a IA possa analisar e identificar os tópicos corretamente. Neste pedido, solicito dois tópicos com base na análise do título, da descrição e dos gêneros dos filmes.</p>{
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like: 'n1, n2, ...n'. Here is a movie info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }<p>O próximo pipeline é o pipeline de inferência, que receberá a solicitação e a enviará para o nosso endpoint <strong>generate_topics_ia</strong> . A resposta gerada pelo modelo será armazenada no campo de resultado.</p>{
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      }<p>Em seguida, temos 3 processadores usados para manipular a resposta e defini-la no campo de tópicos, além de remover os campos temporários que eu criei.</p><p>Ao executar esse pipeline, obteremos os seguintes resultados:</p>{
  "docs": [
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "1",
        "_source": {
          "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
          "model_id": "generate_topics_ia",
          "title": "The Lord of the Rings: The Fellowship of the Ring",
          "genres": [
            "Action",
            "Adventure",
            "Drama"
          ],
          "topics": [
            "Fantasy",
            "Quest"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340010257Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "2",
        "_source": {
          "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
          "model_id": "generate_topics_ia",
          "title": "Interstellar",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "space exploration",
            "human survival"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340413173Z"
        }
      }
    },
    {
      "doc": {
        "_index": "index",
        "_version": "-3",
        "_id": "3",
        "_source": {
          "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
          "model_id": "generate_topics_ia",
          "title": "The Martian",
          "genres": [
            "Adventure",
            "Drama",
            "Sci-Fi"
          ],
          "topics": [
            "survival",
            "ingenuity"
          ]
        },
        "_ingest": {
          "timestamp": "2024-11-22T17:51:51.340427965Z"
        }
      }
    }
  ]
}<p>Note que temos novas categorias que estão mais relacionadas ao contexto dos filmes, embora algumas sejam inicialmente do mesmo gênero.</p><p>Agora podemos usar essas novas categorias e indexá-las juntamente com o documento. Dessa forma, ao gerar as facetas, além da categoria primária, temos subcategorias mais específicas que estão alinhadas com o contexto dos filmes.</p><p>Além disso, é possível vetorizar essas novas categorias e usá-las em buscas vetoriais. Isso significa que as novas categorias não servem apenas como filtros, mas também podem ser usadas para calcular similaridades semânticas com os termos de busca, aumentando ainda mais a relevância dos resultados apresentados.</p><p>Pipeline completo:</p>POST /_ingest/pipeline/_simulate
{
  "pipeline": {
    "processors": [
      {
        "script": {
          "source": """
            ctx.prompt = "You are an expert in semantic analysis and audiovisual content categorization. Your task is to generate only subcategories (max 2 topics) that describe specific aspects of movies based on their genres and descriptions. The output should be like string: 'n1, n2m ...n'. Here is a movies info to analyze: Title: " + ctx.title  + "Genres: " + ctx.genres  + "Description: " + ctx.description;
          """
        }
      },
      {
        "inference": {
          "model_id": "generate_topics_ia",
          "input_output": {
            "input_field": "prompt",
            "output_field": "result"
          }
        }
      },
      {
        "split": {
          "field": "result",
          "target_field": "topics",
          "separator": ", "
        }
      },
      {
        "remove": {
          "field": "result"
        }
      },
      {
        "remove": {
          "field": "prompt"
        }
      }
    ]
  },
  "docs": [
    {
      "_index": "index",
      "_id": "1",
      "_source": {
        "title": "The Lord of the Rings: The Fellowship of the Ring",
        "description": "While Frodo and Sam edge closer to Mordor with the help of the shifty Gollum, the divided fellowship makes a stand against Sauron's new ally, Saruman, and his hordes of Isengard.",
        "genres": [
          "Action",
          "Adventure",
          "Drama"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "2",
      "_source": {
        "title": "Interstellar",
        "description": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    },
    {
      "_index": "index",
      "_id": "3",
      "_source": {
        "title": "The Martian",
        "description": "An astronaut becomes stranded on Mars after his team assume him dead, and must rely on his ingenuity to find a way to signal to Earth that he is alive.",
        "genres": [
          "Adventure", "Drama", "Sci-Fi"
        ]
      }
    }
  ]
}<h2>Conclusão</h2><p>O uso de IA para aprimorar os filtros pode transformar a experiência de busca, tornando os resultados mais específicos e contextuais. Ao contrário das categorias fixas, que costumam ser amplas, as categorias geradas por IA podem refletir melhor o contexto. Por exemplo, ao reclassificar filmes, podemos capturar o contexto que as categorias primárias não abrangem, fornecendo agrupamentos muito mais relevantes.</p><p>Essas novas categorias podem ser adicionadas ao índice não apenas para aprimorar a filtragem por facetas, mas também para possibilitar buscas vetoriais. O resultado é uma experiência de busca mais eficiente, com filtros mais adequados ao contexto.</p><h2>Referências</h2><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/infer-service-openai.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/simulate-pipeline-api.html</a></p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/script-processor.html</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/faceted-search-examples-ai</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Andre Luiz]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2838185214162c8/6a17f8bedbb4ff04affb58a5/25c9f9baa2326b5189ce0b1cc6240475781c755d-721x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 28 Jan 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como usar o conector Elasticsearch Vector Store para o Microsoft Semantic Kernel no desenvolvimento de agentes de IA]]></title>
    <description><![CDATA[O Microsoft Semantic Kernel é um kit de desenvolvimento leve e de código aberto que permite criar facilmente agentes de IA e integrar os modelos de IA mais recentes em seu código C#, Python ou Java. Com o lançamento do Semantic Kernel Elasticsearch Vector Store Connector, os desenvolvedores que usam o Semantic Kernel para criar agentes de IA agora podem integrar o Elasticsearch como um armazenamento vetorial escalável de nível empresarial, continuando a usar as abstrações do Semantic Kernel.]]></description>
    <content:encoded><![CDATA[<p>Em colaboração com a equipe <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> , estamos anunciando a disponibilidade do <a href="https://github.com/elastic/semantic-kernel-net/">Semantic Kernel Elasticsearch Vector Store Connector</a> para usuários <a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">do Microsoft Semantic Kernel</a> (.NET). O Semantic Kernel simplifica a criação de agentes de IA de nível empresarial, incluindo a capacidade de aprimorar grandes modelos de linguagem (LLMs) com respostas mais relevantes e orientadas por dados de um Repositório de Vetores. O Semantic Kernel fornece uma camada de abstração perfeita para interagir com armazenamentos vetoriais como o Elasticsearch, oferecendo recursos essenciais como criar, listar e excluir coleções de registros, além de carregar, recuperar e excluir registros individuais.</p><p><a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/elasticsearch-connector?pivots=programming-language-csharp">O conector Semantic Kernel Elasticsearch Vector Store, pronto para uso,</a> oferece suporte às <a href="https://learn.microsoft.com/en-us/semantic-kernel/concepts/vector-store-connectors/?pivots=programming-language-csharp#the-vector-store-abstraction">abstrações de armazenamento vetorial</a> do Semantic Kernel, o que facilita muito para os desenvolvedores a integração do Elasticsearch como um armazenamento vetorial durante a criação de agentes de IA.</p><p>O Elasticsearch possui uma base sólida na comunidade de código aberto e adotou recentemente a <a href="https://www.elastic.co/blog/elasticsearch-is-open-source-again">licença AGPL</a>. Em conjunto com o Microsoft Semantic Kernel de código aberto, essas ferramentas oferecem uma solução poderosa e pronta para uso empresarial. Você pode começar localmente executando o Elasticsearch em poucos minutos executando este comando <code>curl -fsSL https://elastic.co/start-local | sh </code>(consulte <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html">start-local</a> para obter detalhes) e migrar para versões <a href="https://cloud.elastic.co/registration?onboarding_token=vectorsearch&amp;utm_source=semantickernel&amp;utm_content=documentation">hospedadas na nuvem</a> ou <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.16/install-elasticsearch.html">auto-hospedadas</a> à medida que coloca seus agentes de IA em produção.</p><p>Neste blog, veremos como usar <a href="https://github.com/elastic/semantic-kernel-net/">o conector Elasticsearch Vector Store do Semantic Kernel</a> ao utilizar o Semantic Kernel. Uma versão em Python do conector será disponibilizada futuramente.</p><h2>Cenário geral: Construindo um aplicativo RAG com Semantic Kernel e Elasticsearch.</h2><p>Na seção seguinte, analisaremos um exemplo. Em linhas gerais, estamos construindo um aplicativo RAG (Retrieval Augmented Generation) que recebe uma pergunta do usuário como entrada e retorna uma resposta. Usaremos o Azure OpenAI (também é possível usar <a href="https://devblogs.microsoft.com/semantic-kernel/introducing-new-ollama-connector-for-local-models/">um LLM local</a> ) como LLM, o Elasticsearch como repositório de vetores e o Semantic Kernel (.NET) como framework para integrar todos os componentes.</p><p>Se você não estiver familiarizado com arquiteturas RAG, pode ter uma breve introdução com este artigo: <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag</a>.</p><p>A resposta é gerada pelo LLM, que é alimentado com contexto relevante para a pergunta, obtido do armazenamento de vetores do Elasticsearch. A resposta também inclui a fonte que foi usada como contexto pelo LLM.</p><h3>Exemplo RAG</h3><p>Neste exemplo específico, criamos um aplicativo que permite aos usuários fazer perguntas sobre hotéis armazenados em um banco de dados interno de hotéis. O usuário poderia, por exemplo... Pesquise um hotel específico com base em diferentes critérios ou solicite uma lista de hotéis.</p><p>Para o banco de dados de exemplo, geramos uma <a href="https://github.com/elastic/semantic-kernel-net/blob/main/Elastic.SemanticKernel.Playground/hotels.csv">lista de hotéis</a> contendo 100 entradas. O tamanho da amostra é intencionalmente pequeno para permitir que você experimente a demonstração do conector da maneira mais fácil possível. Em uma aplicação do mundo real, o conector Elasticsearch mostraria suas vantagens sobre outras opções, como a implementação de armazenamento vetorial `InMemory`, especialmente ao trabalhar com quantidades extremamente grandes de dados.</p><p>A aplicação de demonstração completa pode ser encontrada no <a href="https://github.com/elastic/semantic-kernel-net/tree/main/Elastic.SemanticKernel.Playground">repositório</a> do conector de armazenamento vetorial do Elasticsearch.</p><p>Vamos começar adicionando os pacotes NuGet necessários e usando as diretivas ao nosso projeto:</p>dotnet add package "Elastic.Clients.Elasticsearch" -v 8.16.2
dotnet add package "Elastic.SemanticKernel.Connectors.Elasticsearch" -v 0.1.2
dotnet add package "Microsoft.Extensions.Hosting" -v 9.0.0
dotnet add package "Microsoft.SemanticKernel.Connectors.AzureOpenAI" -v 1.30.0
dotnet add package "Microsoft.SemanticKernel.PromptTemplates.Handlebars" -v 1.30.0using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// &gt; The hotel that has a rooftop bar is Skyline Suites. You can find more information about this hotel [here](https://example.com/yz567).<p>A resposta correta refere-se à seguinte entrada em nosso arquivo hotels.csv.</p>9;
Skyline Suites;
Offering panoramic city views from every suite, this hotel is perfect for those who love the urban landscape. Enjoy luxurious amenities, a rooftop bar, and close proximity to attractions. Luxurious and contemporary.;
https://example.com/yz567<p>Este exemplo demonstra muito bem como a utilização do Microsoft Semantic Kernel permite uma redução significativa da complexidade através das suas abstrações bem concebidas, além de proporcionar um elevado nível de flexibilidade. Ao alterar uma única linha de código, por exemplo, o armazenamento de vetores ou os serviços de IA utilizados podem ser substituídos sem a necessidade de refatorar qualquer outra parte do código.</p><p>Ao mesmo tempo, a estrutura fornece um enorme conjunto de funcionalidades de alto nível, como a função `InvokePrompt`, ou o sistema de plugins de modelo ou de pesquisa.</p><p>A aplicação de demonstração completa pode ser encontrada no repositório do conector de armazenamento vetorial do Elasticsearch.</p><h2>O que mais é possível fazer com o Elasticsearch?</h2><ul><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-search-simplified-semantic-text">Novo mapeamento semantic_text do Elasticsearch: Simplificando a busca semântica</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/semantic-reranking-with-retrievers">Reclassificação semântica no Elasticsearch com retrievers</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">Técnicas avançadas de RAG, parte 1: Processamento de dados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Técnicas avançadas de RAG, parte 2: Consultas e testes</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elasticsearch-rag-with-llama3-opensource-and-elastic">Construindo o RAG com Llama 3 de código aberto e Elastic</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/local-rag-agent-elasticsearch-langgraph-llama3">Um tutorial sobre como construir um agente local usando LangGraph, LLaMA3 e o armazenamento de vetores Elasticsearch do zero.</a></p></li></ul><h2>Elasticsearch e Semantic Kernel: O que vem a seguir?</h2><ul><li><p>Mostramos como o armazenamento de vetores do Elasticsearch pode ser facilmente integrado ao Semantic Kernel durante a criação de aplicações GenAI em .NET. Fique atento para a integração com Python em breve.</p></li><li><p>À medida que o Semantic Kernel cria abstrações para recursos de pesquisa avançados, como <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/vector-search/hybrid-search">a pesquisa híbrida</a>, a conexão com o Elasticsearch permitirá que os desenvolvedores .NET os implementem facilmente ao usar o Semantic Kernel.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-connector-microsoft-semantic-kernel</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[.NET]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[Florian Bernd,Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d8725035e86f8a8/6a17fe447f6f1564f8c09d74/0564fe794e4c66d0507317822d7aa71826183d20-1311x762.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 06 Dec 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[LangChain4j com Elasticsearch como armazenamento de incorporação]]></title>
    <description><![CDATA[O LangChain4j (LangChain para Java) utiliza o Elasticsearch como armazenamento integrado. Descubra como usá-lo para construir sua aplicação RAG em Java puro.]]></description>
    <content:encoded><![CDATA[<p>
Na <a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">publicação anterior</a>, descobrimos o que é LangChain4j e como:</p><ul><li><p>Inicie uma discussão com os LLMs implementando um <code>ChatLanguageModel</code> e um <code>ChatMemory</code></p></li><li><p>Manter o histórico do chat na memória para relembrar o contexto de uma discussão anterior com um LLM</p></li></ul><p>Esta postagem do blog aborda como:</p><ul><li><p>Criar vetores incorporados a partir de exemplos de texto</p></li><li><p>Armazene os vetores de incorporação no repositório de incorporações do Elasticsearch. </p></li><li><p>Buscar vetores semelhantes</p></li></ul><h2>Criar incorporações</h2><p>Para criar embeddings, precisamos definir um <code>EmbeddingModel</code> para usar. Por exemplo, podemos usar o mesmo modelo mistral que usamos na <a href="https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction">postagem anterior</a>. Estava correndo com a lhama:</p>EmbeddingModel model = OllamaEmbeddingModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();<p>Um modelo é capaz de gerar vetores a partir de texto. Aqui podemos verificar o número de dimensões geradas pelo modelo:</p>Logger.info("Embedding model has {} dimensions.", model.dimension());
// This gives: Embedding model has 4096 dimensions.<p>Para gerar vetores a partir de um texto, podemos usar:</p>Response&lt;Embedding&gt; response = model.embed("A text here");<p>Ou, se também quisermos fornecer metadados para nos permitir filtrar por coisas como texto, preço, data de lançamento ou qualquer outra coisa, podemos usar <code>Metadata.from()</code>. Por exemplo, estamos adicionando aqui o nome do jogo como um campo de metadados:</p>TextSegment game1 = TextSegment.from("""
    The game starts off with the main character Guybrush Threepwood stating "I want to be a pirate!"
    To do so, he must prove himself to three old pirate captains. During the perilous pirate trials, 
    he meets the beautiful governor Elaine Marley, with whom he falls in love, unaware that the ghost pirate 
    LeChuck also has his eyes on her. When Elaine is kidnapped, Guybrush procures crew and ship to track 
    LeChuck down, defeat him and rescue his love.
""", Metadata.from("gameName", "The Secret of Monkey Island"));
Response&lt;Embedding&gt; response1 = model.embed(game1);
TextSegment game2 = TextSegment.from("""
    Out Run is a pseudo-3D driving video game in which the player controls a Ferrari Testarossa 
    convertible from a third-person rear perspective. The camera is placed near the ground, simulating 
    a Ferrari driver's position and limiting the player's view into the distance. The road curves, 
    crests, and dips, which increases the challenge by obscuring upcoming obstacles such as traffic 
    that the player must avoid. The object of the game is to reach the finish line against a timer.
    The game world is divided into multiple stages that each end in a checkpoint, and reaching the end 
    of a stage provides more time. Near the end of each stage, the track forks to give the player a 
    choice of routes leading to five final destinations. The destinations represent different 
    difficulty levels and each conclude with their own ending scene, among them the Ferrari breaking 
    down or being presented a trophy.
""", Metadata.from("gameName", "Out Run"));
Response&lt;Embedding&gt; response2 = model.embed(game2);<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step5EmbedddingsTest.java">Step5EmbedddingsTest.java</a> .</p><h2>Adicionar o Elasticsearch para armazenar nossos vetores.</h2><p>LangChain4j fornece um armazenamento de incorporação em memória. Isso é útil para executar testes simples:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore = new InMemoryEmbeddingStore&lt;&gt;();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Mas, obviamente, isso não funcionaria com conjuntos de dados muito maiores, porque esse armazenamento de dados guarda tudo na memória e não temos memória infinita em nossos servidores. Assim, poderíamos armazenar nossos embeddings no Elasticsearch, que é, por definição, "elástico" e pode ser dimensionado verticalmente e horizontalmente conforme a demanda de dados. Para isso, vamos adicionar o Elasticsearch ao nosso projeto:</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;elasticsearch&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;<p>Como você deve ter notado, também adicionamos o módulo Elasticsearch TestContainers ao projeto, para que possamos iniciar uma instância do Elasticsearch a partir de nossos testes:</p>// Create the elasticsearch container
ElasticsearchContainer container =
  new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:8.15.0")
    .withPassword("changeme");

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

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

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

// Check the cluster is running
client.performRequest(new Request("GET", "/"));<p>Para usar o Elasticsearch como um armazenamento de dados incorporado, você "simplesmente" precisa trocar o armazenamento de dados em memória do LangChain4j pelo armazenamento de dados do Elasticsearch:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Isso armazenará seus vetores no Elasticsearch em um índice <code>default</code> . Você também pode alterar o nome do índice para algo mais significativo:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .indexName("games")
    .restClient(client)
    .build();
embeddingStore.add(response1.content(), game1);
embeddingStore.add(response2.content(), game2);<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step6ElasticsearchEmbedddingsTest.java">Step6ElasticsearchEmbedddingsTest.java</a> .</p><h2>Buscar vetores semelhantes</h2><p>Para buscar vetores semelhantes, primeiro precisamos transformar nossa pergunta em uma representação vetorial usando o mesmo modelo que usamos anteriormente. Já fizemos isso, então não é difícil fazer de novo. Note que, neste caso, não precisamos dos metadados:</p>String question = "I want to pilot a car";
Embedding questionAsVector = model.embed(question).content();<p>Podemos construir uma solicitação de pesquisa com essa representação da nossa pergunta e pedir ao repositório de embeddings para encontrar os primeiros vetores principais:</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Agora podemos iterar sobre os resultados e imprimir algumas informações, como o nome do jogo, que vem dos metadados, e a pontuação:</p>result.matches().forEach(m -&gt; Logger.info("{} - score [{}]",
  m.embedded().metadata().getString("gameName"), m.score()));<p>Como era de se esperar, isso nos dá "Out Run" como o primeiro sucesso:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ca0dcfdb1a9c94f/6a170291cf4f256938b2d017/140b6a962e5edbb4870419250e30bfb815b0d73e-640x480.gif" alt="Out Run" />Out Run - score [0.86672974]
The Secret of Monkey Island - score [0.85569763]<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L110-L129">Step7SearchForVectorsTest.java</a> . </p><h2>Nos bastidores</h2><p>A configuração padrão do Elasticsearch Embedding Store utiliza a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-knn-query.html">consulta kNN aproximada</a> nos bastidores.</p>POST games/_search
{
  "query" : {
    "knn": {
      "field": "vector",
      "query_vector": [-0.019137882, /* ... */, -0.0148779955]
    }
  }
}<p>Mas isso poderia ser alterado fornecendo uma configuração diferente (<code>ElasticsearchConfigurationScript</code>) da configuração padrão (<code>ElasticsearchConfigurationKnn</code>) para o armazenamento de incorporação:</p>EmbeddingStore&lt;TextSegment&gt; embeddingStore =
  ElasticsearchEmbeddingStore.builder()
    .configuration(ElasticsearchConfigurationScript.builder().build())
    .indexName("games")
    .restClient(client)
    .build();<p>A implementação <code>ElasticsearchConfigurationScript</code> executa em segundo plano uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html">consulta</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html"><code>script_score</code></a> usando uma <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine">função</a> <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl-script-score-query.html#vector-functions-cosine"><code>cosineSimilarity</code></a> .</p><p>Basicamente, ao ligar:</p>EmbeddingSearchResult&lt;TextSegment&gt; result = embeddingStore.search(
  EmbeddingSearchRequest.builder()
    .queryEmbedding(questionAsVector)
    .build());<p>Isto agora exige:</p>POST games/_search
{
  "query": {
    "script_score": {
      "script": {
        "source": "(cosineSimilarity(params.query_vector, 'vector') + 1.0) / 2",
        "params": {
          "queryVector": [-0.019137882, /* ... */, -0.0148779955]
        }
      }
    }
  }
}<p>Nesse caso, o resultado não muda em termos de "ordem", apenas a pontuação é ajustada, pois a chamada <code>cosineSimilarity</code> não usa nenhuma aproximação, mas calcula o cosseno para cada um dos vetores correspondentes:</p>Out Run - score [0.871952]
The Secret of Monkey Island - score [0.86380446]<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/9ec4b1d4c7c69821f143ddf272bbfed273c67b14/src/test/java/fr/pilato/demo/Step7SearchForVectorsTest.java#L132-L155">Step7SearchForVectorsTest.java</a> .</p><h2>Conclusão</h2><p>Já abordamos a facilidade com que você pode gerar embeddings a partir do seu texto e como você pode armazenar e pesquisar os vizinhos mais próximos no Elasticsearch usando duas abordagens diferentes:</p><ul><li><p>Usando a consulta aproximada e rápida <code>knn</code> com a opção padrão <code>ElasticsearchConfigurationKnn</code></p></li><li><p>Usando a consulta exata, porém mais lenta, <code>script_score</code> com a opção <code>ElasticsearchConfigurationScript</code></p></li></ul><p>O próximo passo será construir uma aplicação RAG completa, com base no que aprendemos aqui.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfc873b86c76d1798/6a170293acf088f666be99b3/abd8a4a809064101c037af66b87f28e5ecde03b0-1474x645.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 08 Oct 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Apresentamos o LangChain4j para simplificar a integração do LLM em aplicações Java.]]></title>
    <description><![CDATA[LangChain4j (LangChain para Java) é um conjunto de ferramentas poderoso para construir sua aplicação RAG em Java puro.]]></description>
    <content:encoded><![CDATA[<p>O <a href="https://docs.langchain4j.dev/">framework LangChain4j</a> foi criado em 2023 com <a href="https://github.com/langchain4j/langchain4j/blob/main/README.md#introduction">este objetivo</a>:</p>O objetivo do LangChain4j é simplificar a integração de LLMs em aplicações Java.<p>LangChain4j oferece uma maneira padronizada de:</p><ul><li><p>Criar representações vetoriais (embeddings) a partir de um determinado conteúdo, digamos, um texto, por exemplo.</p></li><li><p>armazenar embeddings em um armazenamento de embeddings</p></li><li><p>pesquisar vetores semelhantes no armazenamento de embeddings</p></li><li><p>discutir com mestres em direito</p></li><li><p>Use uma memória de bate-papo para lembrar o contexto de uma discussão com um mestre em Direito.</p></li></ul><p>Esta lista não é exaustiva e a comunidade LangChain4j está sempre implementando novos recursos.</p><p>Esta postagem abordará as primeiras partes principais da estrutura.</p><h2>Adicionando LangChain4j OpenAI ao nosso projeto</h2><p>Como em todos os projetos Java, é apenas uma questão de dependências. Aqui usaremos o Maven, mas o mesmo resultado poderia ser obtido com qualquer outro gerenciador de dependências.</p><p>Como primeiro passo para o projeto que queremos construir aqui, usaremos o OpenAI, então precisamos apenas adicionar o artefato <code>langchain4j-open-ai</code> :</p>&lt;properties&gt;
  &lt;langchain4j.version&gt;0.34.0&lt;/langchain4j.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
  &lt;dependency&gt;
    &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
    &lt;artifactId&gt;langchain4j-open-ai&lt;/artifactId&gt;
    &lt;version&gt;${langchain4j.version}&lt;/version&gt;
  &lt;/dependency&gt;
&lt;/dependencies&gt;
<p>Para o restante do código, usaremos nossa própria chave de API, que você pode obter criando uma conta na <a href="https://platform.openai.com/signup/">OpenAI</a>, ou a fornecida pelo projeto LangChain4j apenas para fins de demonstração:</p>static String getOpenAiApiKey() {
  String apiKey = System.getenv(API_KEY_ENV_NAME);
  if (apiKey == null || apiKey.isEmpty()) {
    Logger.warn("Please provide your own key instead using [{}] env variable", API_KEY_ENV_NAME);
    return "demo";
  }
  return apiKey;
}
<p>Agora podemos criar uma instância do nosso ChatLanguageModel:</p>ChatLanguageModel model = OpenAiChatModel.withApiKey(getOpenAiApiKey());
<p>E, finalmente, podemos fazer uma pergunta simples e obter a resposta:</p>String answer = model.generate("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>A resposta fornecida poderia ser algo como:</p>Thomas Pesquet is a French aerospace engineer, pilot, and European Space Agency astronaut.
He was selected as a member of the European Astronaut Corps in 2009 and has since completed 
two space missions to the International Space Station, including serving as a flight engineer 
for Expedition 50/51 in 2016-2017. Pesquet is known for his contributions to scientific 
research and outreach activities during his time in space.
<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step1AiChatTest.java">Step1AiChatTest.java</a> .</p><h2>Fornecendo mais contexto com langchain4j</h2><p>Vamos adicionar o artefato <code>langchain4j</code> :</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>Esta ferramenta oferece um conjunto de recursos que pode nos ajudar a construir uma integração LLM mais avançada para criar nosso assistente. Aqui, criaremos uma interface <code>Assistant</code> que fornece o método <code>chat</code> , o qual chamará automaticamente o <code>ChatLanguageModel</code> que definimos anteriormente:</p>interface Assistant {
  String chat(String userMessage);
}
<p>Basta pedirmos à classe LangChain4j <code>AiServices</code> para construir uma instância para nós:</p>Assistant assistant = AiServices.create(Assistant.class, model);
<p>Em seguida, chame o método <code>chat(String)</code> :</p>String answer = assistant.chat("Who is Thomas Pesquet?");
Logger.info("Answer is: {}", answer);
<p>Está apresentando o mesmo comportamento de antes. Então, por que alteramos o código? Em primeiro lugar, é mais elegante, mas além disso, agora você pode dar instruções ao LLM usando anotações simples:</p>interface Assistant {
  @SystemMessage("Please answer in a funny way.")
  String chat(String userMessage);
}
<p>Isso está resultando em:</p>Ah, Thomas Pesquet is actually a super secret spy disguised as an astronaut! 
He's out there in space fighting aliens and saving the world one spacewalk at a time. 
Or maybe he's just a really cool French astronaut who has been to the International 
Space Station. But my spy theory is much more exciting, don't you think?
<p>Se você quiser executar este código, consulte a classe <a href="https://github.com/dadoonet/langchain4j-demo/blob/main/src/test/java/fr/pilato/demo/Step2AssistantTest.java">Step2AssistantTest.java</a> .</p><h2>Mudando para outro LLM: langchain4j-ollama</h2><p>Podemos usar o excelente <a href="https://ollama.com/">projeto Ollama</a>. Executar um LLM localmente em sua máquina pode ser útil.</p><p>Vamos adicionar o artefato <code>langchain4j-ollama</code> :</p>&lt;dependency&gt;
  &lt;groupId&gt;dev.langchain4j&lt;/groupId&gt;
  &lt;artifactId&gt;langchain4j-ollama&lt;/artifactId&gt;
  &lt;version&gt;${langchain4j.version}&lt;/version&gt;
&lt;/dependency&gt;
<p>Como estamos executando o código de exemplo usando testes, vamos adicionar <a href="https://java.testcontainers.org/">Testcontainers</a> ao nosso projeto:</p>&lt;dependency&gt;
  &lt;groupId&gt;org.testcontainers&lt;/groupId&gt;
  &lt;artifactId&gt;ollama&lt;/artifactId&gt;
  &lt;version&gt;1.20.1&lt;/version&gt;
  &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
<p>Agora podemos iniciar/parar contêineres Docker:</p>static String MODEL_NAME = "mistral";
static String DOCKER_IMAGE_NAME = "langchain4j/ollama-" + MODEL_NAME + ":latest";

static OllamaContainer ollama = new OllamaContainer(
  DockerImageName.parse(DOCKER_IMAGE_NAME).asCompatibleSubstituteFor("ollama/ollama"));

@BeforeAll
public static void setup() {
  ollama.start();
}

@AfterAll
public static void teardown() {
  ollama.stop();
}
<p>Nós "apenas" precisamos mudar o objeto <code>model</code> para se tornar um <code>OllamaChatModel</code> em vez do <code>OpenAiChatModel</code> que usamos anteriormente:</p>OllamaChatModel model = OllamaChatModel.builder()
  .baseUrl(ollama.getEndpoint())
  .modelName(MODEL_NAME)
  .build();
<p>Observe que pode levar algum tempo para obter a imagem com seu modelo, mas depois de um tempo, você poderá obter a resposta:</p>Oh, Thomas Pesquet, the man who single-handedly keeps the French space program running 
while sipping on his crisp rosé and munching on a baguette! He's our beloved astronaut 
with an irresistible accent that makes us all want to learn French just so we can 
understand him better. When he's not floating in space, he's probably practicing his 
best "je ne sais quoi" face for the next family photo. Vive le Thomas Pesquet! 
🚀🌍🇫🇷 #FrenchSpaceHero
<h2>Melhor com memória</h2><p>Se fizermos várias perguntas, por padrão o sistema não se lembrará das perguntas e respostas anteriores. Então, se perguntarmos depois da primeira pergunta "Quando ele nasceu?", Nosso aplicativo responderá:</p>Oh, you're asking about this legendary figure from history, huh? Well, let me tell 
you a hilarious tale! He was actually born on Leap Year's Day, but only every 400 
years! So, do the math... if we count backwards from 2020 (which is also a leap year), 
then he was born in... *drumroll please* ...1600! Isn't that a hoot? But remember 
folks, this is just a joke, and historical records may vary.
<p>O que é um absurdo. Em vez disso, devemos usar <a href="https://docs.langchain4j.dev/tutorials/chat-memory">a Memória de Chat</a>:</p>ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
Assistant assistant = AiServices.builder(Assistant.class)
  .chatLanguageModel(model)
  .chatMemory(chatMemory)
  .build();
<p>Executar as mesmas perguntas agora fornece uma resposta significativa:</p>Oh, Thomas Pesquet, the man who was probably born before sliced bread but after dinosaurs! 
You know, around the time when people started putting wheels on suitcases and calling it 
a revolution. So, roughly speaking, he came into this world somewhere in the late 70s or 
early 80s, give or take a year or two - just enough time for him to grow up, become an 
astronaut, and make us all laugh with his space-aged antics! Isn't that a hoot? 
*laughs maniacally*
<h2>Conclusão</h2><p>Na <a href="https://www.elastic.co/search-labs/blog/langchain4j-elasticsearch-embedding-store">próxima publicação</a>, descobriremos como podemos fazer perguntas ao nosso conjunto de dados privado usando o Elasticsearch como repositório de embeddings. Isso nos dará uma maneira de elevar nossa busca de aplicativos a um novo patamar.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/langchain4j-llm-integration-introduction</guid>
    <category><![CDATA[Java]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[David Pilato]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0435ed6d14579089/6a17e79ae8fbce7e433a192f/cf129b8b25fbe7204e2adca8fca5fec04207f096-720x720.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Sep 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Técnicas avançadas de RAG, parte 2: Consultas e testes]]></title>
    <description><![CDATA[Discutir e implementar técnicas que possam aumentar o desempenho do RAG. Parte 2 de 2, com foco em consultas e testes de um pipeline RAG avançado.]]></description>
    <content:encoded><![CDATA[<p><em>Todo o código pode ser encontrado </em><a href="https://github.com/elastic/elasticsearch-labs/tree/advanced-rag-techniques/supporting-blog-content/advanced-rag-techniques"><em>no repositório Searchlabs, na branch advanced-rag-techniques</em></a><em>.</em></p><p>Bem-vindo(a) à Parte 2 do nosso artigo sobre Técnicas Avançadas de RAG! Na <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1">parte 1 desta série</a>, configuramos, discutimos e implementamos os componentes de processamento de dados do pipeline RAG avançado:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" alt="Gasoduto RAG avançado" /><p>Nesta parte, vamos prosseguir com a consulta e o teste da nossa implementação. Vamos direto ao assunto!</p><h3>Índice</h3><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#searching-and-retrieving,-generating-answers">Pesquisar e recuperar, gerar respostas</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#enriching-queries-with-synonyms">Enriquecendo as consultas com sinônimos</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-hypothetical-document-embedding">HyDE (Incorporação Hipotética de Documentos)</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hybrid-search">Busca híbrida</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#experiments">Experimentos</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#summary-of-results">Resumo dos resultados</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-1-who-audits-elastic">Teste 1: Quem audita a Elastic?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-2--total-revenue-2023">Teste 2: receita total em 2023</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-1">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-1">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-3-what-product-does-growth-primarily-depend-on-how-much">Teste 3: De qual produto depende principalmente o crescimento? Quanto?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-2">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-2">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-4-describe-employee-benefit-plan">Teste 4: Descreva o plano de benefícios para funcionários</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-3">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-3">SimpleRAG</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#test-5-which-companies-did-elastic-acquire">Teste 5: Quais empresas a Elastic adquiriu?</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#advancedrag-4">RAG Avançado</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#simplerag-4">SimpleRAG</a></p></li></ul></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#conclusion">Conclusão</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#appendix">Apêndice</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#prompts">Prompts</a></p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#rag-question-answering-prompt">Pergunta RAG para responder:</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#elastic-query-generator-prompt">prompt do gerador de consultas elásticas</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#potential-questions-generator-prompt">Possíveis perguntas para o gerador</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#hyde-generator-prompt">prompt do gerador HyDE</a></p></li></ul></li><li><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2#sample-hybrid-search-query">Exemplo de consulta de pesquisa híbrida</a></p></li></ul></li></ul><h2>Pesquisar e recuperar, gerar respostas</h2><p>Vamos fazer nossa primeira pergunta, idealmente alguma informação encontrada principalmente no relatório anual. Que tal:</p>Who audits Elastic?"
<p>Agora, vamos aplicar algumas de nossas técnicas para aprimorar a consulta.</p><h3>Enriquecendo as consultas com sinônimos</h3><p>Em primeiro lugar, vamos aumentar a diversidade na formulação da consulta e transformá-la em um formato que possa ser facilmente processado em uma consulta do Elasticsearch. Vamos utilizar o GPT-4o para converter a consulta em uma lista de cláusulas OR. Vamos escrever esta pergunta:</p>
ELASTIC_SEARCH_QUERY_GENERATOR_PROMPT = '''
You are an AI assistant specialized in generating Elasticsearch query strings. Your task is to create the most effective query string for the given user question. This query string will be used to search for relevant documents in an Elasticsearch index.

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

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

AND operator is not allowed. Use only OR.

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

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

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

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

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

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

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

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

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

    embedding=embedder.get_embeddings_from_text(text_query)

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

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

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

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

    hyde_document=gpt4o.generate_HyDE(query_text)

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

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

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

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

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

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

Additionally, it is mentioned here as well:

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

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

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

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

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

From the context:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Context:
[The concatenated documents will be inserted here]

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

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

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

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

AND operator is not allowed. Use only OR.

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

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

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

Document:
[The document content will be inserted here]

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import uuid
import re


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

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

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

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

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

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

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

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

                    chunked_documents.append(chunk_doc)

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

        return chunked_documents

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

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

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

class DocumentEnricher:

    def __init__(self):
        pass 

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

# Initialize LLM
documentenricher=DocumentEnricher()

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

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

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

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

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

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

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

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

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


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

success_count = es_bulk_indexer.bulk_upload_documents(
    index_name=index_name, 
    documents=chunked_documents, 
    id_col='id_',
    batch_size=32
)
<p>Acesse o Kibana e verifique se todos os documentos foram indexados. Deveriam ser 224. Nada mal para um documento tão grande!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8efeface6effe01d/6a170b447d8d67652870e72a/1b3b07f6b98ceb65f6594ce4be83c5b0ed7e7cf9-1440x1380.jpg" alt="Índice Kibana" /><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p><h2>Pausa para o gato</h2><p>Vamos fazer uma pausa, o artigo está um pouco denso, eu sei. Vejam só o meu gato:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc1db5595f71c12ff/6a170b450e2e49940241a0fe/baca4eb52b801b21ced97352cc55462f0a12d6b0-969x996.jpg" alt="Gasoduto Han" /><p>Adorável. O chapéu sumiu e eu suspeito que ela o roubou e escondeu em algum lugar :(</p><p>Parabéns por ter chegado até aqui :)</p><p>Junte-se a mim na <a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-2">Parte 2</a> para testes e avaliação do nosso pipeline RAG!</p><h2>Apêndice</h2><h3>Definições</h3><p><strong>1. Segmentação de Frases</strong></p><ul><li><p>Uma técnica de pré-processamento usada em sistemas RAG para dividir o texto em unidades menores e significativas.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Entrada: Bloco grande de texto (ex.: documento, parágrafo)</p></li><li><p>Saída: Segmentos de texto menores (normalmente frases ou pequenos grupos de frases)</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Cria segmentos de texto granulares e específicos ao contexto.</p></li><li><p>Permite uma indexação e recuperação mais precisas.</p></li><li><p>Melhora a relevância das informações recuperadas em sistemas RAG.</p></li></ul></li><li><p><em>Características:</em> </p><ul><li><p>Os segmentos têm significado semântico.</p></li><li><p>Podem ser indexados e recuperados independentemente.</p></li><li><p>Frequentemente preserva algum contexto para garantir a compreensibilidade de forma independente.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Aumenta a precisão de recuperação</p></li><li><p>Permite uma ampliação mais focada em pipelines RAG.</p></li></ul></li></ul><p><strong>2. HyDE (Incorporação Hipotética de Documentos)</strong></p><ul><li><p>Uma técnica que utiliza um modelo de lógica latente (LLM) para gerar um documento hipotético para expansão de consultas em sistemas RAG.</p></li><li><p><em>Processo:</em>  </p><ol><li><p>Inserir consulta em um LLM</p></li><li><p>O LLM gera um documento hipotético que responde à pergunta.</p></li><li><p>Incorpore o documento gerado</p></li><li><p>Use o embedding para busca vetorial</p></li></ol></li><li><p><em>Principal diferença:</em> </p><ul><li><p>RAG tradicional: relaciona a consulta aos documentos.</p></li><li><p>HyDE: Correspondência entre documentos.</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhorar o desempenho de recuperação de dados, especialmente para consultas complexas ou ambíguas.</p></li><li><p>Captura um contexto semântico mais rico do que uma consulta curta.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Aproveita o conhecimento do LLM para ampliar as consultas.</p></li><li><p>Pode potencialmente melhorar a relevância dos documentos recuperados.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Requer inferência LLM adicional, aumentando a latência e o custo.</p></li><li><p>O desempenho depende da qualidade do documento hipotético gerado.</p></li></ul></li></ul><p><strong>3. Reembalagem reversa</strong></p><ul><li><p>Uma técnica utilizada em sistemas RAG para reordenar os resultados da pesquisa antes de passá-los para o LLM.</p></li><li><p><em>Processo:</em> </p><ol><li><p>O mecanismo de busca (por exemplo, Elasticsearch) retorna documentos em ordem decrescente de relevância.</p></li><li><p>A ordem é invertida, colocando o documento mais relevante por último.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Explora o viés de recência dos mestrados em direito, que tendem a se concentrar mais nas informações mais recentes em seu contexto.</p></li><li><p>Garante que as informações mais relevantes sejam as mais "atualizadas" na janela de contexto do LLM.</p></li></ul></li><li><p><em>Exemplo:</em> Ordem original: [Mais relevante, Segundo mais relevante, Terceiro mais relevante, ...] Ordem inversa: [..., Terceiro mais relevante, Segundo mais relevante, Mais relevante]</p></li></ul><p><strong>4. Classificação de consultas</strong></p><ul><li><p>Uma técnica para otimizar a eficiência do sistema RAG, determinando se uma consulta requer RAG ou se pode ser respondida diretamente pelo LLM.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Desenvolver um conjunto de dados personalizado específico para o LLM em uso.</p></li><li><p>Treinar um modelo de classificação especializado</p></li><li><p>Utilize o modelo para categorizar as consultas recebidas.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhore a eficiência do sistema evitando o processamento desnecessário de RAG (raiz, grafite e agregação).</p></li><li><p>Direcione as consultas para o mecanismo de resposta mais apropriado.</p></li></ul></li><li><p><em>Requisitos:</em> </p><ul><li><p>Conjunto de dados e modelo específicos para LLM</p></li><li><p>Aperfeiçoamento contínuo para manter a precisão.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Reduz a sobrecarga computacional para consultas simples.</p></li><li><p>Potencialmente melhora o tempo de resposta para consultas que não sejam RAG.</p></li></ul></li></ul><p><strong>5. Resumo</strong></p><ul><li><p>Uma técnica para condensar documentos recuperados em sistemas RAG.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Recuperar documentos relevantes</p></li><li><p>Gere resumos concisos de cada documento.</p></li><li><p>Utilize resumos em vez de documentos completos no pipeline RAG.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Melhore o desempenho RAG concentrando-se em informações essenciais.</p></li><li><p>Reduzir o ruído e a interferência de conteúdo menos relevante</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora a relevância das respostas do LLM</p></li><li><p>Permite a inclusão de mais documentos dentro dos limites do contexto.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Risco de perder detalhes importantes na sumarização.</p></li><li><p>Sobrecarga computacional adicional para geração de resumos.</p></li></ul></li></ul><p><strong>6. Inclusão de Metadados</strong></p><ul><li><p>Uma técnica para enriquecer documentos com informações contextuais adicionais.</p></li><li><p><em>Tipos de metadados:</em>  </p><ul><li><p>Palavras-chave</p></li><li><p>Títulos</p></li><li><p>Datas</p></li><li><p>Detalhes da autoria</p></li><li><p>Resumos</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Aumentar a informação contextual disponível para o sistema RAG</p></li><li><p>Proporcionar aos alunos de mestrado em Direito uma compreensão mais clara do conteúdo e da relevância dos documentos.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora a precisão da recuperação.</p></li><li><p>Aumenta a capacidade do LLM de avaliar a utilidade dos documentos.</p></li></ul></li><li><p><em>Implementação:</em> </p><ul><li><p>Pode ser feito durante o pré-processamento do documento.</p></li><li><p>Pode exigir etapas adicionais de extração ou geração de dados.</p></li></ul></li></ul><p><strong>7. Incorporações compostas de múltiplos campos</strong></p><ul><li><p>Uma técnica avançada de incorporação para sistemas RAG que cria incorporações separadas para diferentes componentes do documento.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Identifique os campos relevantes (ex.: título, palavras-chave, sinopse, conteúdo principal).</p></li><li><p>Gere embeddings separados para cada campo.</p></li><li><p>Combine ou armazene esses embeddings para uso na recuperação de informações.</p></li></ol></li><li><p><em>Diferença em relação à abordagem padrão:</em> </p><ul><li><p>Tradicional: Incorporação única para todo o documento.</p></li><li><p>Composição: Incorporação múltipla para diferentes aspectos do documento</p></li></ul></li><li><p><em>Propósito:</em> </p><ul><li><p>Criar representações de documentos mais matizadas e sensíveis ao contexto.</p></li><li><p>Capturar informações de uma variedade maior de fontes em um documento.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Potencialmente melhora o desempenho em consultas ambíguas ou multifacetadas.</p></li><li><p>Permite uma ponderação mais flexível de diferentes aspectos do documento na recuperação.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Aumento da complexidade na incorporação de processos de armazenamento e recuperação</p></li><li><p>Pode exigir algoritmos de correspondência mais sofisticados.</p></li></ul></li></ul><p><strong>8. Enriquecimento de consultas</strong></p><ul><li><p>Uma técnica para expandir a consulta original com termos relacionados, a fim de melhorar o alcance da pesquisa.</p></li><li><p><em>Processo:</em> </p><ol><li><p>Analise a consulta original</p></li><li><p>Gere sinônimos e frases semanticamente relacionadas.</p></li><li><p>Aprimore a consulta com estes termos adicionais.</p></li></ol></li><li><p><em>Propósito:</em> </p><ul><li><p>Aumentar o leque de correspondências potenciais no conjunto de documentos.</p></li><li><p>Melhorar o desempenho de recuperação de dados para consultas com linguagem específica ou técnica.</p></li></ul></li><li><p><em>Benefícios:</em> </p><ul><li><p>Pode recuperar documentos relevantes que não correspondam exatamente aos termos da consulta original.</p></li><li><p>Pode ajudar a superar a incompatibilidade de vocabulário entre consultas e documentos.</p></li></ul></li><li><p><em>Desafios:</em> </p><ul><li><p>Risco de desvio de consulta se não for implementado com cuidado.</p></li><li><p>Pode aumentar a sobrecarga computacional no processo de recuperação.</p></li></ul></li></ul><p><a href="https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1#table-of-contents">Voltar ao topo</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/advanced-rag-techniques-part-1</guid>
    <category><![CDATA[Banco de dados vetorial]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Han Xiang Choong]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9a4691874a19d8da/6a170b3f47d49c99f22d8a24/72b51ba2ae5e5977b56e5b915674753d6cfd0e56-1440x840.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 14 Aug 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Protegendo informações sensíveis e de identificação pessoal (PII) no RAG com Elasticsearch e LlamaIndex.]]></title>
    <description><![CDATA[Como proteger dados sensíveis e informações pessoais identificáveis (PII) em uma aplicação RAG com Elasticsearch e LlamaIndex.]]></description>
    <content:encoded><![CDATA[<p></p><p></p><p>Neste artigo, analisaremos maneiras de proteger informações de identificação pessoal (PII) e dados sensíveis ao usar LLMs públicos em um fluxo RAG (Retrieval Augmented Generation). Vamos explorar a mascaramento de informações pessoais identificáveis (PII) e dados sensíveis usando bibliotecas de código aberto e expressões regulares, bem como o uso de LLMs locais para mascarar dados antes de invocar um LLM público.</p><p>Antes de começarmos, vamos revisar alguns termos que usamos nesta postagem.</p><h2>Terminologia</h2><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> é uma estrutura de dados líder para a construção de aplicações LLM (Large Language Model). O LlamaIndex fornece abstrações para vários estágios de construção de um aplicativo RAG (Retrieval Augmented Generation). Frameworks como LlamaIndex e LangChain fornecem abstrações para que os aplicativos não fiquem fortemente acoplados às APIs de nenhum LLM específico.</p><p><a href="https://www.elastic.co/enterprise-search">O Elasticsearch</a> é oferecido pela <a href="https://elastic.co/">Elastic</a>. A Elastic é líder do setor e está por trás do Elasticsearch, um armazenamento de dados escalável e banco de dados vetorial que oferece suporte a pesquisa de texto completo para precisão, pesquisa vetorial para compreensão semântica e pesquisa híbrida para o melhor dos dois mundos. O Elasticsearch é um mecanismo de busca e análise distribuído e baseado em REST, um armazenamento de dados escalável e um banco de dados vetorial. Os recursos do Elasticsearch que usamos neste blog estão disponíveis na versão gratuita e de código aberto do Elasticsearch.</p><p><a href="https://www.promptingguide.ai/techniques/rag">A Geração Aumentada por Recuperação (RAG, na sigla em inglês)</a> é uma técnica/padrão de IA em que os Modelos de Aprendizagem Baseados em Aprendizagem (LLMs, na sigla em inglês) recebem conhecimento externo para gerar respostas às consultas do usuário. Isso permite que as respostas do LLM sejam adaptadas a um contexto específico e menos genéricas.</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">Os embeddings</a> são representações numéricas do significado de um texto/mídia. São representações de menor dimensão de informações de alta dimensão.</p><h2>RAG e Proteção de Dados</h2><p>De modo geral, os Modelos de Linguagem de Grande Porte (LLMs, na sigla em inglês) são bons em gerar respostas com base nas informações disponíveis no modelo, que pode ser treinado com dados da internet. No entanto, para as consultas em que as informações não estão disponíveis no modelo, os LLMs precisam receber conhecimento externo ou detalhes específicos não contidos no modelo. Essas informações podem estar em seu banco de dados ou sistema de conhecimento interno. A Geração Aumentada por Recuperação (RAG, na sigla em inglês) é uma técnica na qual, para uma determinada consulta do usuário, você primeiro recupera o contexto/informação relevante de sistemas externos (aos LLMs), como seu banco de dados, e envia esse contexto juntamente com a consulta do usuário para o LLM para gerar uma resposta mais específica e relevante.</p><p>Isso torna a técnica RAG altamente eficaz para aplicações em perguntas e respostas, criação de conteúdo e em qualquer situação em que uma compreensão profunda do contexto e dos detalhes seja benéfica.</p><p>Consequentemente, em um pipeline RAG, você corre o risco de expor informações internas, como PII (Informações de Identificação Pessoal) e informações sensíveis (por exemplo, nomes, datas de nascimento, números de contas etc.) a servidores públicos.</p><p>Embora seus dados estejam seguros ao usar um banco de dados vetorial como o Elasticsearch (através de várias ferramentas como <a href="https://www.elastic.co/guide/en/cloud-enterprise/current/ece-configure-rbac.html">Controle de Acesso Baseado em Funções</a>, <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">Segurança em Nível de Documento</a> etc.), é preciso ter cuidado ao enviar dados para um repositório público de dados.</p><p>A proteção de informações de identificação pessoal (PII) e dados sensíveis é crucial ao usar modelos de linguagem de grande porte (LLMs) por diversos motivos:</p><ul><li><p><strong>Conformidade com a privacidade</strong>: Muitas regiões possuem regulamentações rigorosas, como o Regulamento Geral de Proteção de Dados (RGPD) na Europa ou a Lei de Privacidade do Consumidor da Califórnia (CCPA) nos Estados Unidos, que exigem a proteção de dados pessoais. O cumprimento dessas leis é necessário para evitar consequências legais e multas.</p></li><li><p><strong>Confiança do usuário</strong>: Garantir a confidencialidade e a integridade de informações sensíveis constrói a confiança do usuário. Os usuários tendem a usar e interagir mais com sistemas que acreditam proteger sua privacidade.</p></li><li><p><strong>Segurança de dados</strong>: a proteção contra violações de dados é essencial. Dados sensíveis expostos a sistemas de gestão de dados sem salvaguardas adequadas podem ficar suscetíveis a roubo ou uso indevido, levando a potenciais danos como roubo de identidade ou fraude financeira.</p></li><li><p><strong>Considerações éticas</strong>: Do ponto de vista ético, é importante respeitar a privacidade dos usuários e tratar seus dados de forma responsável. O manuseio inadequado de informações pessoais identificáveis pode levar à discriminação, estigmatização ou outros impactos sociais negativos.</p></li><li><p><strong>Reputação empresarial</strong>: Empresas que não protegem dados sensíveis podem sofrer danos à sua reputação, o que pode ter efeitos negativos a longo prazo em seus negócios, incluindo perda de clientes e receita.</p></li><li><p><strong>Redução dos riscos de abuso</strong>: O manuseio seguro de dados sensíveis ajuda a prevenir o uso malicioso dos dados ou do modelo, como o treinamento de modelos com dados tendenciosos ou o uso dos dados para manipular ou prejudicar indivíduos.</p></li></ul><p>De modo geral, a proteção robusta de informações pessoais identificáveis e dados sensíveis é necessária para garantir a conformidade legal, manter a confiança do usuário, assegurar a segurança dos dados, defender os padrões éticos, proteger a reputação da empresa e reduzir o risco de abuso.</p><h2>Resumo rápido</h2><p>Na <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch">publicação anterior,</a> discutimos como implementar uma experiência de perguntas e respostas usando a técnica RAG com o Elasticsearch como banco de dados vetorial, utilizando o LlamaIndex e um Mistral LLM executado localmente. Aqui, damos continuidade a isso.</p><p>A leitura da postagem anterior é opcional, pois agora vamos discutir/recapitular rapidamente o que fizemos na postagem anterior.</p><p>Tínhamos um conjunto de dados de amostra de conversas de call center entre agentes e clientes de uma empresa fictícia de seguros residenciais. Criamos um aplicativo RAG simples que respondia a perguntas como "Que tipo de problemas relacionados à água os clientes estão relatando?".</p><p>Em linhas gerais, o fluxo de trabalho se apresentava assim.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Fluxo RAG" /><p>Durante a fase de indexação, carregamos e indexamos documentos usando o pipeline LlamaIndex. Os documentos foram divididos em partes e armazenados no banco de dados vetorial Elasticsearch juntamente com seus respectivos embeddings.</p><p>Durante a fase de consulta, quando o usuário fazia uma pergunta, o LlamaIndex recuperava os K documentos mais semelhantes e relevantes para a consulta. Esses K documentos mais relevantes, juntamente com a consulta, foram enviados ao Mistral LLM em execução localmente, que então gerou a resposta a ser enviada de volta ao usuário. Fique à vontade para ler a postagem anterior ou <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/main">explorar o código</a>.</p><p>Na postagem anterior, tínhamos o LLM rodando localmente. No entanto, em produção, você pode querer usar um LLM externo fornecido por várias empresas como <a href="https://openai.com/">OpenAI</a>, <a href="https://mistral.ai/">Mistral</a>, <a href="https://www.anthropic.com/claude">Anthropic</a> etc. Isso pode ocorrer porque seu caso de uso exige um modelo fundamental maior ou porque a execução local não é uma opção devido a necessidades de produção corporativa, como escalabilidade, disponibilidade, desempenho etc.</p><p>A introdução de um LLM externo em seu pipeline RAG expõe você ao risco de vazamento inadvertido de informações sensíveis e PII para os LLMs. Neste post, exploraremos opções sobre como mascarar informações de identificação pessoal (PII) como parte do seu fluxo de trabalho RAG antes de enviar documentos para um LLM externo.</p><h2>RAG com um mestrado em Direito público</h2><p>Antes de discutirmos como proteger suas informações pessoais e sensíveis em um pipeline RAG, primeiro construiremos um aplicativo RAG simples usando LlamaIndex, banco de dados Elasticsearch Vector e OpenAI LLM.</p><h3>Pré-requisitos</h3><p>Precisaremos do seguinte:</p><ul><li><p><strong>O Elasticsearch</strong> está configurado e funcionando como banco de dados vetorial para armazenar os embeddings. Siga as instruções da postagem anterior sobre <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#install-elasticsearch">como instalar o Elasticsearch</a>.</p></li><li><p>Chaves de API OpenAI.</p></li></ul><h3>Aplicação RAG simples</h3><p>Para referência, o código completo pode ser encontrado neste <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/tree/protecting-pii">repositório do Github</a>(branch:protecting-pii). Clonar o repositório é opcional, pois analisaremos o código a seguir.</p><p>Em sua IDE favorita, crie uma nova aplicação Python com os 3 arquivos abaixo.</p><ul><li><p><code>index.py</code> Onde fica o código relacionado à indexação de dados.</p></li><li><p><code>query.py</code> Onde fica o código relacionado a consultas e interação com o LLM.</p></li><li><p><code>.env</code> onde ficam as propriedades de configuração, como chaves de API.</p></li></ul><p>Precisamos instalar alguns pacotes. Começamos criando um novo <a href="https://docs.python.org/3/library/venv.html">ambiente virtual</a> Python na pasta raiz da sua aplicação.</p>python3 -m venv .venv
<p>Ative o ambiente virtual e instale os pacotes necessários listados abaixo.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-openai
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
pip install openai
<p>Configure as propriedades de conexão do OpenAI e do Elasticsearch no arquivo .env. arquivo.</p>OPENAI_API_KEY="REPLACEME"
ELASTIC_CLOUD_ID="REPLACEME"
ELASTIC_API_KEY="REPLACEME"
<h4>Dados de indexação</h4><p>Baixe o arquivo <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">conversations.json</a> , que contém <em>conversas</em> entre clientes e atendentes do call center da nossa fictícia empresa de seguros residenciais. Coloque o arquivo no diretório raiz da aplicação, junto com os dois arquivos Python e o arquivo .env. arquivo que você criou anteriormente. Segue abaixo um exemplo do conteúdo do arquivo.</p>{
"conversation_id": 103,
"customer_name": "Sophia Jones",
"agent_name": "Emily Wilson",
"policy_number": "JKL0123",
"conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
"summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Cole o código abaixo em <code>index.py</code> que cuida da indexação dos dados.</p># index.py
# pip install sentence-transformers
# pip install llama-index-embeddings-openai
# pip install llama-index-embeddings-huggingface

import json
import os
from dotenv import load_dotenv
from llama_index.core import Document
from llama_index.core import Settings
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import SentenceSplitter
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore


def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())

   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents

# Load .env file contents into env
load_dotenv('.env')
Settings.embed_model = HuggingFaceEmbedding(
   model_name="BAAI/bge-small-en-v1.5"
)

def main():
   # ElasticsearchStore is a VectorStore that
   # takes care of Elasticsearch Index and Data management.
   es_vector_store = ElasticsearchStore(index_name="convo_index",
                                        vector_field='conversation_vector',
                                        text_field='conversation',
                                        es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                        es_api_key=os.getenv("ELASTIC_API_KEY"))

   # LlamaIndex Pipeline configured to take care of chunking, embedding
   # and storing the embeddings in the vector store.
   llamaindex_pipeline = IngestionPipeline(
       transformations=[
           SentenceSplitter(chunk_size=350, chunk_overlap=50),
           Settings.embed_model
       ],
       vector_store=es_vector_store
   )

   # Load data from a json file into a list of LlamaIndex Documents
   documents = get_documents_from_file(file="conversations.json")
   llamaindex_pipeline.run(documents=documents)
   print(".....Indexing Data Completed.....\n")

if __name__ == "__main__":
   main()
<p>Executar o código acima cria um índice no Elasticsearch, armazenando os embeddings no índice do Elasticsearch chamado <code>convo_index</code>.</p><p>Caso precise de explicações sobre o IngestionPipeline do LlamaIndex, consulte a publicação anterior na seção <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#indexing-data">Criar IngestionPipeline</a>.</p><h4>Consultando</h4><p>Na postagem anterior, usamos um LLM local para <a href="https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch#querying">consulta</a>.</p><p>Neste post, utilizamos o LLM público da OpenAI, conforme mostrado abaixo.</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Public LLM where we send user query and Related Documents
llm = OpenAI()

index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are sent as-is. So any PII/Sensitive data is sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10)

query="Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>O código acima imprime a resposta da OpenAI conforme abaixo.</p><p>Os clientes apresentaram diversas reclamações relacionadas a problemas com água, incluindo danos causados por água em porões, canos estourados, danos causados por granizo em telhados e recusa de indenizações por motivos como falta de notificação em tempo hábil, problemas de manutenção, desgaste gradual e danos preexistentes. Em todos os casos, os clientes expressaram frustração com as negativas de seus pedidos e buscaram avaliações e decisões justas em relação às suas reivindicações.</p><h2>Mascaramento de informações pessoais identificáveis (PII) em RAG</h2><p>O que abordamos até agora envolve o envio de documentos tal como estão para a OpenAI, juntamente com a consulta do usuário.</p><p>No pipeline RAG, após a recuperação do contexto relevante de um repositório Vector, temos a oportunidade de mascarar informações pessoais identificáveis (PII) e informações sensíveis antes de enviar a consulta e o contexto para o LLM.</p><p>Existem várias maneiras de mascarar informações pessoais identificáveis (PII) antes de enviá-las a um LLM externo, cada uma com seus próprios méritos. Analisamos algumas das opções abaixo.</p><ol><li><p>Utilizando bibliotecas de PNL como spacy.io ou <a href="https://microsoft.github.io/presidio/">Presidio</a> (biblioteca de código aberto mantida pela Microsoft).</p></li><li><p>Utilizando o LlamaIndex sem nenhuma configuração adicional. <code>NERPIINodePostprocessor.</code></p></li><li><p>Utilizando LLMs locais via <code>PIINodePostprocessor</code></p></li></ol><p>Após implementar a lógica de mascaramento usando qualquer um dos métodos acima, você pode configurar o IngestionPipeline do LlamaIndex com um PostProcessor (um personalizado ou qualquer um dos PostProcessors prontos para uso do LlamaIndex).</p><h3>Utilizando bibliotecas de PNL</h3><p>Como parte do pipeline RAG, poderíamos mascarar dados sensíveis usando bibliotecas de PNL (Processamento de Linguagem Natural). Nesta demonstração, usaremos o pacote spacy.io.</p><p>Crie um novo arquivo <code>query_masking_nlp.py</code> e adicione o código abaixo.</p># query_masking_nlp.py

# pip install spacy
# python3 - m spacy download en_core_web_sm
import re
from typing import List, Optional

import spacy
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor.types import BaseNodePostprocessor
from llama_index.core.schema import NodeWithScore
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

# Load the spaCy model
nlp = spacy.load("en_core_web_sm")

# Compile regex patterns for performance
phone_pattern = re.compile(r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b')
email_pattern = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')
date_pattern = re.compile(r'\b(\d{1,2}[-/]\d{1,2}[-/]\d{2,4}|\d{2,4}[-/]\d{1,2}[-/]\d{1,2})\b')
dob_pattern = re.compile(
r"(January|February|March|April|May|June|July|August|September|October|November|December)\s(\d{1,2})(st|nd|rd|th),\s(\d{4})")
address_pattern = re.compile(r'\d+\s+[\w\s]+\,\s+[A-Za-z]+\,\s+[A-Z]{2}\s+\d{5}(-\d{4})?')
zip_code_pattern =  re.compile(r'\b\d{5}(?:-\d{4})?\b')
policy_number_pattern = re.compile(r"[A-Z]{3}\d{4}\.$")  # 3 characters followed by 4 digits, in our case e.g XYZ9876

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# match = re.match(policy_number_pattern, "XYZ9876")
# print(match)


def mask_pii(text):
   """
   Masks Personally Identifiable Information (PII) in the given
   text using pre-defined regex patterns and spaCy's named entity recognition.
   Args:
       text (str): The input text containing potential PII.
   Returns:
       str: The text with PII masked.
   """

   # Process the text with spaCy for NER
   doc = nlp(text)

   # Mask entities identified by spaCy NER (e.g First/Last Names etc)
   for ent in doc.ents:
       if ent.label_ in ["PERSON", "ORG", "GPE"]:
           text = text.replace(ent.text, '[MASKED]')

   # Apply regex patterns after NER to avoid overlapping issues
   text = phone_pattern.sub('[PHONE MASKED]', text)
   text = email_pattern.sub('[EMAIL MASKED]', text)
   text = date_pattern.sub('[DATE MASKED]', text)
   text = address_pattern.sub('[ADDRESS MASKED]', text)
   text = dob_pattern.sub('[DOB MASKED]', text)
   text = zip_code_pattern.sub('[ZIP MASKED]', text)
   text = policy_number_pattern.sub('[POLICY MASKED]', text)

   return text


class CustomPostProcessor(BaseNodePostprocessor):
   """
   Custom Postprocessor which masks Personally Identifiable Information (PII).
   PostProcessor is called on the Documents before they are sent to the LLM.
   """
   def _postprocess_nodes(
           self, nodes: List[NodeWithScore], query_bundle: Optional[QueryBundle]
   ) -&gt; List[NodeWithScore]:
       # Masks PII
       for n in nodes:
          n.node.set_content(mask_pii(n.text))
       return nodes

   
# Use Public LLM to send user query and Related Documents
llm = OpenAI()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents are masked based on custom logic defined in CustomPostProcessor._postprocess_nodes.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[CustomPostProcessor()])



query = "Give me summary of water related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)

<p>A resposta do LLM é apresentada abaixo.</p>Os clientes apresentaram diversas reclamações relacionadas a problemas com água, incluindo danos causados pela água em porões, canos estourados, danos causados por granizo em telhados e inundações durante chuvas fortes. Essas reclamações têm gerado frustrações devido a negativas baseadas em motivos como falta de notificação em tempo hábil, problemas de manutenção, desgaste gradual e danos preexistentes. Os clientes expressaram decepção, estresse e dificuldades financeiras em decorrência dessas negativas de indenização, buscando avaliações justas e análises minuciosas de suas solicitações. Alguns clientes também enfrentaram atrasos no processamento de sinistros, causando ainda mais insatisfação com o serviço prestado pela seguradora.<p>No código acima, ao criar o Llama Index QueryEngine, fornecemos um CustomPostProcessor.</p><p>A lógica que é invocada pelo QueryEngine é definida no método <code>_postprocess_nodes</code> de <code>CustomPostProcessor</code>. Estamos utilizando a biblioteca SpaCy.io para detectar entidades nomeadas em nossos documentos e, em seguida, usamos expressões regulares para substituir esses nomes, bem como informações confidenciais, antes de enviá-los para o LLM.</p><p>Abaixo, seguem como exemplo trechos de conversas originais e da conversa mascarada criada pelo CustomPostProcessor.</p><p>Texto original:</p>Cliente: Olá, meu nome é Matthew Lopez, minha data de nascimento é 12 de outubro de 1984 e moro no endereço 456 Cedar St, Smalltown, NY 34567. Meu número de apólice é TUV8901. Agente: Boa tarde, Matthew. Como posso te ajudar hoje? Cliente: Olá, estou extremamente decepcionado com a decisão da sua empresa de negar minha solicitação.<p>Texto mascarado pelo CustomPostProcessor.</p>Cliente: Olá, meu nome é [MASKED], meu nome é [MASKED] e meu nome é [MASKED], e moro na Rua Cedar, 456, [MASKED], [MASKED] 34567. Meu número de apólice é [MASKED]. Agente: Boa tarde, [MASKED]. Como posso te ajudar hoje? Cliente: Olá, estou extremamente decepcionado com a decisão da sua empresa de negar minha solicitação.<p>Observação:</p><p><em>Identificar e mascarar informações pessoais identificáveis e informações sensíveis não é uma tarefa simples. Lidar com os diversos formatos e semânticas de informações sensíveis exige um bom conhecimento do seu domínio e dos seus dados. Embora o código apresentado acima possa funcionar para alguns casos de uso, talvez seja necessário modificá-lo com base em suas necessidades e testes.</em></p><h3>Utilizando o LlamaIndex sem nenhuma configuração adicional. <code>NERPIINodePostprocessor</code></h3><p>A LlamaIndex facilitou a proteção de informações de identificação pessoal (PII) em um pipeline RAG ao introduzir <code>NERPIINodePostprocessor.</code></p>from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import NERPIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents
llm = OpenAI()

ner_processor = NERPIINodePostprocessor()
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the LLM.
# Note that documents masked using the NERPIINodePostprocessor so that PII/Sensitive data is not sent to the LLM.
query_engine = index.as_query_engine(llm, similarity_top_k=10, node_postprocessors=[ner_processor])

query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
response = query_engine.query(bundle)
print(response)
<p>A resposta é a seguinte:</p>Os clientes apresentaram reclamações relacionadas a incêndios e danos às suas propriedades. Em um dos casos, um pedido de indenização por danos causados por incêndio em uma garagem foi negado devido à exclusão de incêndio criminoso da cobertura. Outro cliente apresentou uma reclamação por danos causados por incêndio em sua casa, que foram cobertos pela apólice. Além disso, um cliente relatou um incêndio na cozinha e recebeu a garantia de que os danos causados pelo fogo estavam cobertos.<h3>Utilizando LLMs locais via <code>PIINodePostprocessor</code></h3><p>Também poderíamos utilizar um LLM executado localmente ou em sua rede privada para realizar o trabalho de mascaramento antes de enviar os dados para um LLM público.</p><p>Usaremos o Mistral, executado no Ollama em sua máquina local, para fazer o mascaramento.</p><h4>Execute o Mistral localmente</h4><p>Baixe e instale <a href="https://ollama.com/">o Ollama</a>. Após instalar o Ollama, execute este comando para baixar e executar <a href="https://ollama.com/library/mistral">o mistral.</a></p>ollama run mistral
<p>Pode levar alguns minutos para baixar e executar o modelo localmente pela primeira vez. Verifique se o mistral está ativo fazendo uma pergunta como a seguinte: "Escreva um poema sobre nuvens" e veja se você gostou do poema. Mantenha o Ollama em execução, pois precisaremos interagir com o modelo Mistral posteriormente por meio de código.</p><p>Crie um novo arquivo chamado <code>query_masking_local_LLM.py</code> e adicione o código abaixo.</p># pip install llama-index-llms-ollama
from llama_index.core import VectorStoreIndex, QueryBundle, Settings
from llama_index.core.postprocessor import PIINodePostprocessor
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.ollama import Ollama
from llama_index.llms.openai import OpenAI
from index import es_vector_store

Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

# Use Public LLM to send user query and Related Documents and Local LLM to mask
public_llm = OpenAI()
local_llm = Ollama(model="mistral")

pii_processor = PIINodePostprocessor(llm=local_llm)
index = VectorStoreIndex.from_vector_store(es_vector_store)

# This query_engine, for a given user query retrieves top 10 similar documents from
# Elasticsearch vector database and sends the documents along with the user query to the public LLM.
# Note that documents are masked using the local llm via PIINodePostprocessor
# so that PII/Sensitive data is not sent to the public LLM.
query_engine = index.as_query_engine(public_llm, similarity_top_k=10, node_postprocessors=[pii_processor])


query = "Give me summary of fire related claims that customers raised."
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>A resposta será algo semelhante ao que é mostrado abaixo.</p>Os clientes apresentaram reclamações relacionadas a incêndios e danos às suas propriedades. Em um dos casos, um pedido de indenização por danos causados por incêndio em uma garagem foi negado devido à exclusão de incêndio criminoso da cobertura. Outro cliente apresentou uma reclamação por danos causados por incêndio em sua casa, que foram cobertos pela apólice. Além disso, um cliente relatou um incêndio na cozinha e recebeu a garantia de que os danos causados pelo fogo estavam cobertos.<h3>Conclusão</h3><p>Neste post, mostramos como você pode proteger informações pessoais identificáveis (PII) e dados sensíveis ao usar LLMs públicos em um fluxo RAG. Demonstramos diversas maneiras de alcançar esse objetivo. É altamente recomendável testar essas abordagens com base em seu caso de uso e necessidades antes de adotá-las.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-security-masking-pii</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-security-masking-pii</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Jul 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[RAG (Retrieval Augmented Generation) com LlamaIndex, Elasticsearch e Mistral]]></title>
    <description><![CDATA[Aprenda como implementar um sistema RAG (Retrieval Augmented Generation) usando LlamaIndex, Elasticsearch e o Mistral em execução local.]]></description>
    <content:encoded><![CDATA[<p>Neste blog, discutiremos como implementar uma experiência de perguntas e respostas usando a técnica RAG (Retrieval Augmented Generation) com o Elasticsearch como banco de dados vetorial. Usaremos o LlamaIndex e uma instância local do Mistral LLM.</p><p>Antes de começarmos, vamos analisar alguns termos.</p><h3>Terminologia</h3><p><a href="https://www.llamaindex.ai/">LlamaIndex</a> é uma estrutura de dados líder para a construção de aplicações LLM (Large Language Model). O LlamaIndex fornece abstrações para vários estágios de construção de um aplicativo RAG (Retrieval Augmented Generation). Frameworks como LlamaIndex e LangChain fornecem abstrações para que os aplicativos não fiquem fortemente acoplados às APIs de nenhum LLM específico.</p><p><a href="https://www.elastic.co/enterprise-search">O Elasticsearch</a> é oferecido pela <a href="https://elastic.co/">Elastic</a>. A Elastic é líder do setor e está por trás do Elasticsearch, um mecanismo de busca e análise que oferece suporte à busca de texto completo para precisão, busca vetorial para compreensão semântica e busca híbrida para o melhor dos dois mundos. O Elasticsearch é um banco de dados vetorial e um repositório de dados escalável. As funcionalidades do Elasticsearch que utilizamos neste blog estão disponíveis na versão gratuita e de código aberto do Elasticsearch.</p><p><a href="https://www.promptingguide.ai/techniques/rag">A Geração Aumentada de Recuperação (RAG, na sigla em inglês)</a> é uma técnica/padrão de IA em que os Modelos de Aprendizagem Baseados em Aprendizagem (LLMs, na sigla em inglês) recebem conhecimento externo para gerar respostas às consultas do usuário. Isso permite que as respostas do LLM sejam adaptadas a contextos específicos e sejam mais detalhadas.</p><p><a href="https://docs.mistral.ai/">A Mistral</a> oferece modelos LLM de código aberto e modelos otimizados para uso empresarial. Neste tutorial, usaremos o modelo de código aberto <a href="https://docs.mistral.ai/models/#mistral-7b">mistral-7b</a> , que funciona no seu computador portátil. Se você não quiser executar o modelo no seu laptop, como alternativa, pode usar a versão em nuvem. Nesse caso, você precisará modificar o código deste blog para usar as chaves de API e os pacotes corretos.</p><p><a href="https://ollama.com/">O Ollama</a> ajuda a executar LLMs localmente no seu laptop. Usaremos o Ollama para executar o modelo de código aberto Mistral-7b localmente.</p><p><a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.13/semantic-search.html">Os embeddings</a> são representações numéricas do significado de um texto/mídia. São representações de menor dimensão de informações de alta dimensão.</p><h3>Construindo uma aplicação RAG com LlamaIndex, Elasticsearch e Mistral: Visão geral do cenário</h3><p><strong>Cenário:</strong></p><p>Temos um conjunto de dados de amostra (em formato JSON) de conversas de call center entre agentes e clientes de uma empresa fictícia de seguros residenciais. Vamos construir um aplicativo RAG simples que possa responder a perguntas como:</p><p><code>Give me summary of water related issues.</code></p><h3>Fluxo de alto nível</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" alt="Fluxo RAG" /><p>Temos o Mistral LLM instalado e funcionando localmente usando o Ollama.</p><p>Em seguida, carregamos <em>as conversas</em> do arquivo JSON como <code>Documents</code> no <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a> (que é um VectorStore com suporte do Elasticsearch). Ao carregar os documentos, criamos embeddings usando o modelo Mistral executado localmente. Armazenamos esses embeddings juntamente com as <em>conversas</em> no LlamaIndex Elasticsearch vector Store (<a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>).</p><p>Configuramos um pipeline de ingestão LlamaIndex e o abastecemos com o LLM local que utilizamos, neste caso o Mistral executado via Ollama.</p><p>Quando fazemos uma pergunta como "Dê-me um resumo dos problemas relacionados à água", O Elasticsearch realiza uma busca semântica e retorna <em>conversas</em> relacionadas a questões hídricas. Essas <em>conversas,</em> juntamente com a pergunta original, são enviadas ao LLM local para gerar uma resposta.</p><h3>Etapas para construir o aplicativo RAG</h3><h4>Execute o Mistral localmente</h4><p>Baixe e instale <a href="https://ollama.com/">o Ollama</a>. Após instalar o Ollama, execute este comando para baixar e executar <a href="https://ollama.com/library/mistral">o mistral.</a></p>ollama run mistral
<p>Pode levar alguns minutos para baixar e executar o modelo localmente pela primeira vez. Verifique se o mistral está ativo fazendo uma pergunta como a seguinte: "Escreva um poema sobre nuvens" e veja se você gostou do poema. Mantenha o Ollama em execução, pois precisaremos interagir com o modelo Mistral posteriormente por meio de código.</p><h4>Instale o Elasticsearch</h4><p>Instale e execute o Elasticsearch criando uma implantação na nuvem (<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">instruções aqui</a>) ou executando-o em um contêiner Docker (<a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker">instruções aqui</a>). Você também pode criar uma implantação auto-hospedada de nível de produção do Elasticsearch, começando <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/docker#self-hosted-production-deployments">aqui</a>.</p><p>Supondo que você esteja usando a implantação em nuvem, obtenha a chave da API e o ID da nuvem para a implantação, conforme mencionado nas instruções. Vamos usá-los mais tarde.</p><h4>aplicação RAG</h4><p>Para referência, o código completo pode ser encontrado neste <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany">repositório do Github</a>. Clonar o repositório é opcional, pois analisaremos o código a seguir.</p><p>Em sua IDE favorita, crie uma nova aplicação Python com os 3 arquivos abaixo.</p><ul><li><p><code>index.py</code> Onde fica o código relacionado à indexação de dados.</p></li><li><p><code>query.py</code> Onde fica o código relacionado a consultas e interação com o LLM.</p></li><li><p><code>.env</code> onde ficam as propriedades de configuração, como chaves de API.</p></li></ul><p>Precisamos instalar alguns pacotes. Começamos criando um novo <a href="https://docs.python.org/3/library/venv.html">ambiente virtual</a> Python na pasta raiz da sua aplicação.</p>python3 -m venv .venv
<p>Ative o ambiente virtual e instale os pacotes necessários listados abaixo.</p>source .venv/bin/activate
pip install llama-index 
pip install llama-index-embeddings-ollama
pip install llama-index-llms-ollama
pip install llama-index-vector-stores-elasticsearch
pip install sentence-transformers
pip install python-dotenv
<h4>Dados de indexação</h4><p>Baixe o arquivo <a href="https://github.com/srikanthmanvi/RAG-InsuranceCompany/blob/main/conversations.json">conversations.json</a> , que contém <em>conversas</em> entre clientes e atendentes de call center da nossa fictícia empresa de seguros residenciais. Coloque o arquivo no diretório raiz da aplicação, junto com os dois arquivos Python e o arquivo .env. arquivo que você criou anteriormente. Segue abaixo um exemplo do conteúdo do arquivo.</p>{
    "conversation_id": 103,
    "customer_name": "Sophia Jones",
    "agent_name": "Emily Wilson",
    "policy_number": "JKL0123",
    "conversation": "Customer: Hi, I'm Sophia Jones. My Date of Birth is November 15th, 1985, Address is 303 Cedar St, Miami, FL 33101, and my Policy Number is JKL0123.\nAgent: Hello, Sophia. How may I assist you today?\nCustomer: Hello, Emily. I have a question about my policy.\nCustomer: There's been a break-in at my home, and some valuable items are missing. Are they covered?\nAgent: Let me check your policy for coverage related to theft.\nAgent: Yes, theft of personal belongings is covered under your policy.\nCustomer: That's a relief. I'll need to file a claim for the stolen items.\nAgent: We'll assist you with the claim process, Sophia. Is there anything else I can help you with?\nCustomer: No, that's all for now. Thank you for your assistance, Emily.\nAgent: You're welcome, Sophia. Please feel free to reach out if you have any further questions or concerns.\nCustomer: I will. Have a great day!\nAgent: You too, Sophia. Take care.",
    "summary": "A customer inquires about coverage for stolen items after a break-in at home, and the agent confirms that theft of personal belongings is covered under the policy. The agent offers assistance with the claim process, resulting in the customer expressing relief and gratitude."
}
<p>Definimos uma função chamada <code>get_documents_from_file</code> em <code>index.py</code> que lê o arquivo json e cria uma lista de Documentos. Os objetos <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/documents_and_nodes/">de documento</a> são a unidade básica de informação com a qual o LlamaIndex trabalha.</p># index.py
import json, os
from llama_index.core import Document, Settings
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.vector_stores.elasticsearch import ElasticsearchStore
from dotenv import load_dotenv

def get_documents_from_file(file):
   """Reads a json file and returns list of Documents"""

   with open(file=file, mode='rt') as f:
       conversations_dict = json.loads(f.read())
      
   # Build Document objects using fields of interest.
   documents = [Document(text=item['conversation'],
                         metadata={"conversation_id": item['conversation_id']})
                for
                item in conversations_dict]
   return documents
<p>Criar Pipeline de Ingestão</p><p>Primeiramente, adicione o CloudID do Elasticsearch e as chaves de API que você obteve na seção <code>Install Elasticsearch</code> ao arquivo <code>.env</code> . Seu arquivo <code>.env</code> deve ter a aparência abaixo (com valores reais).</p>ELASTIC_CLOUD_ID=&lt;REPLACE WITH YOUR CLOUD ID&gt;
ELASTIC_API_KEY=&lt;REPLACE WITH YOUR API_KEY&gt;
<p>O LlamaIndex <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/ingestion_pipeline/">IngestionPipeline</a> permite compor um pipeline usando vários componentes. Adicione o código abaixo ao arquivo <code>index.py</code> .</p># index.py

# Load .env file contents into env
# ELASTIC_CLOUD_ID and ELASTIC_API_KEY are expected to be in the .env file.
load_dotenv('.env')

# ElasticsearchStore is a VectorStore that
# takes care of ES Index and Data management.
es_vector_store = ElasticsearchStore(index_name="calls",
                                     vector_field='conversation_vector',
                                     text_field='conversation',
                                     es_cloud_id=os.getenv("ELASTIC_CLOUD_ID"),
                                     es_api_key=os.getenv("ELASTIC_API_KEY"))


def main():
    # Embedding Model to do local embedding using Ollama.
    ollama_embedding = OllamaEmbedding("mistral")

    # LlamaIndex Pipeline configured to take care of chunking, embedding
    # and storing the embeddings in the vector store.
    pipeline = IngestionPipeline(
        transformations=[
            SentenceSplitter(chunk_size=350, chunk_overlap=50),
            ollama_embedding,
        ],
        vector_store=es_vector_store
    )

    # Load data from a json file into a list of LlamaIndex Documents
    documents = get_documents_from_file(file="conversations.json")

    pipeline.run(documents=documents)
    print(".....Done running pipeline.....\n")


if __name__ == "__main__":
    main()

<p>Conforme mencionado anteriormente, o LlamaIndex IngestPipeline pode ser composto por vários componentes. Estamos adicionando 3 componentes ao pipeline na linha <code>pipeline = IngestionPipeline(...</code>.</p><ul><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">SentenceSplitter</a>: Como pode ser visto na definição de <code>get_documents_from_file()</code>, cada Documento tem um campo de texto que contém a conversa encontrada no arquivo json. Este campo de texto contém um texto longo. Para que a busca semântica funcione bem, ela precisa ser dividida em blocos de texto menores. A classe <a href="https://docs.llamaindex.ai/en/stable/module_guides/loading/node_parsers/modules/?h=sentencesp#sentencesplitter">SentenceSplitter</a> faz isso por nós. Esses fragmentos são chamados de Nós na terminologia do LlamaIndex. Existem metadados nos nós que apontam de volta para o Documento ao qual pertencem. Alternativamente, você pode usar o Elasticsearch Ingestpipeline para fragmentação, conforme mostrado neste <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">blog</a>.</p></li><li><p><a href="https://docs.llamaindex.ai/en/stable/module_guides/models/embeddings/">OllamaEmbedding</a>: Os modelos de incorporação convertem um texto em números (também chamados de vetores). A representação numérica nos permite realizar <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/semantic-search.html">buscas semânticas,</a> em que os resultados correspondem ao significado da palavra, em vez de apenas realizar uma busca textual. Fornecemos o IngestionPipeline com <code>OllamaEmbedding("mistral")</code>. Os trechos que dividimos usando o SentenceSplitter são enviados para o modelo Mistral que está sendo executado em sua máquina local por meio do Ollama. O Mistral então cria embeddings para esses trechos.</p></li><li><p><a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">ElasticsearchStore</a>: O armazenamento vetorial LlamaIndex ElasticsearchStore faz backup dos embeddings que estão sendo criados em um índice do Elasticsearch. O ElasticsearchStore se encarrega de criar e preencher o conteúdo do índice Elasticsearch especificado. Ao criar o ElasticsearchStore (referenciado por <code>es_vector_store</code>) fornecemos o nome do índice Elasticsearch que queremos criar (<code>calls</code> no nosso caso ), o campo no índice onde queremos que os embeddings sejam armazenados (<code>conversation_vector</code> no nosso caso ) e o campo onde queremos armazenar o texto (<code>conversation</code> no nosso caso ). Em resumo, com base na nossa configuração <code>ElasticsearchStore</code> cria um novo índice no Elasticsearch com <code>conversation_vector</code> e <code>conversation</code> como campos (entre outros campos criados automaticamente).</p></li></ul><p>Unindo tudo, executamos o pipeline chamando <code>pipeline.run(documents=documents)</code>.</p><p>Execute o script index.py para iniciar o pipeline de ingestão:</p>python index.py
<p>Assim que a execução do pipeline for concluída, devemos ver um novo índice no Elasticsearch chamado <code>calls</code>. Ao executar uma consulta simples no Elasticsearch usando o Console do Desenvolvedor, você deverá conseguir ver os dados carregados juntamente com os embeddings.</p>GET calls/_search?size=1
<p>Resumindo o que fizemos até agora, criamos documentos a partir de um arquivo JSON, dividimos esses documentos em partes, criamos embeddings para essas partes e armazenamos os embeddings (e a conversa de texto) em um armazenamento vetorial (ElasticsearchStore).</p><h4>Consultando</h4><p>O llamaIndex <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">VectorStoreIndex</a> permite recuperar documentos relevantes e consultar dados. Por padrão, o VectorStoreIndex armazena embeddings na memória em um <a href="https://docs.llamaindex.ai/en/stable/module_guides/indexing/vector_store_guide/">SimpleVectorStore</a>. No entanto, armazenamentos vetoriais externos (como <a href="https://developers.llamaindex.ai/python/examples/vector_stores/elasticsearchindexdemo/">o ElasticsearchStore</a>) podem ser usados para tornar os embeddings persistentes.</p><p>Abra o <code>query.py</code> e cole o código abaixo.</p># query.py
from llama_index.core import VectorStoreIndex, QueryBundle, Response, Settings
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.llms.ollama import Ollama
from index import es_vector_store

# Local LLM to send user query to
local_llm = Ollama(model="mistral")
Settings.embed_model= OllamaEmbedding("mistral")

index = VectorStoreIndex.from_vector_store(es_vector_store)
query_engine = index.as_query_engine(local_llm, similarity_top_k=10)

query="Give me summary of water related issues"
bundle = QueryBundle(query, embedding=Settings.embed_model.get_query_embedding(query))
result = query_engine.query(bundle)
print(result)
<p>Definimos um LLM local (<code>local_llm</code>) para apontar para o modelo Mistral em execução no Ollama. Em seguida, criamos um VectorStoreIndex (<code>index</code>) a partir do armazenamento de vetores ElasticssearchStore que criamos anteriormente e, em seguida, obtemos um mecanismo de consulta do índice. Ao criar o mecanismo de consulta, fazemos referência ao LLM local que deve ser usado para responder, também fornecemos (<code>similarity_top_k=10</code>) para configurar o número de documentos que devem ser recuperados do armazenamento de vetores e enviados ao LLM para obter uma resposta.</p><p>Execute o script <code>query.py</code> para executar o fluxo RAG:</p>python query.py
<p>Enviamos a consulta <code>Give me summary of water related issues</code> (sinta-se à vontade para personalizar o <code>query</code>) e a resposta do LLM, que é fornecida com os documentos relacionados, deve ser algo como o abaixo.</p>No contexto apresentado, observamos diversos casos em que os clientes perguntaram sobre a cobertura para danos relacionados à água. Em dois casos, as inundações causaram danos aos porões e, em outro caso, o problema foram os vazamentos no telhado. Os agentes confirmaram que ambos os tipos de danos causados pela água estão cobertos pelas respectivas apólices. Portanto, problemas relacionados à água, incluindo inundações e vazamentos no telhado, geralmente são cobertos por apólices de seguro residencial.<h4>Algumas ressalvas:</h4><p>Este post do blog é uma introdução para iniciantes à técnica RAG com Elasticsearch e, portanto, omite a configuração de recursos que permitirão que você leve este ponto de partida para um ambiente de produção. Ao desenvolver soluções para uso em produção, você deve considerar aspectos mais sofisticados, como a capacidade de proteger seus dados com <a href="https://www.elastic.co/search-labs/blog/dls-internal-knowledge-search">Segurança em Nível de Documento</a>, dividir seus dados em partes menores como parte de um <a href="https://www.elastic.co/search-labs/blog/chunking-via-ingest-pipelines">pipeline de ingestão</a> do Elasticsearch ou até mesmo executar outras <a href="https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-overview.html">tarefas de aprendizado de máquina</a> nos mesmos dados usados para casos de uso de GenAI/Chat/Perguntas e Respostas.</p><p>Você também pode considerar a possibilidade de obter dados e criar embeddings a partir de várias fontes externas (por exemplo, Azure Blob Storage, Dropbox, Gmail etc.) usando <a href="https://www.elastic.co/guide/en/enterprise-search/current/connectors.html">Elastic Connectors</a>.</p><p>A Elastic torna tudo isso e muito mais possível, oferecendo uma solução abrangente de nível empresarial para casos de uso de GenAI e além.</p><h4>O que vem a seguir?</h4><ul><li><p>Você deve ter notado que estamos enviando 10 conversas relacionadas, juntamente com a pergunta do usuário, para o LLM para que ele elabore uma resposta. Essas conversas podem conter informações de identificação pessoal (PII), como nome, data de nascimento, endereço etc. No nosso caso, o LLM é local, portanto o vazamento de dados não é um problema. No entanto, quando se deseja usar um LLM executado na nuvem (por exemplo, OpenAI), não é recomendável enviar textos que contenham informações de identificação pessoal. Em uma postagem de acompanhamento, veremos como realizar o mascaramento de informações PII antes de enviá-las a LLMs externos no fluxo RAG.</p></li><li><p>Neste post, usamos um LLM local. No próximo post sobre mascaramento de dados PII no RAG, veremos como podemos migrar facilmente de um LLM local para um LLM público.</p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/rag-with-llamaIndex-and-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Srikanth Manvi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d3882da43bfdac0/6a17050867045bd5fe45c0e9/9d51295472f8bcca3d1973248acb724f8b94767e-1054x555.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Apr 2024 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>