<?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[JD Armada - 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[JD Armada - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/pt/search-labs/author/jd-armada</link>
    </image>
    <link>https://www.elastic.co/pt/search-labs/author/jd-armada</link>
    <atom:link href="https://www.elastic.co/pt/search-labs/rss/author/jd-armada.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[pt]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 02:38:20 GMT</lastBuildDate>
  <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[Construindo um agente de conhecimento com recuperação semântica usando Mastra e Elasticsearch.]]></title>
    <description><![CDATA[Aprenda como construir um agente de conhecimento com recuperação semântica usando Mastra e Elasticsearch como armazenamento vetorial para memória e recuperação de informações.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">A Engenharia de Contexto</a> está se tornando cada vez mais importante na construção de agentes e arquiteturas de IA confiáveis. À medida que os modelos se tornam cada vez melhores, sua eficácia e confiabilidade dependem menos dos dados de treinamento e mais de quão bem eles estão fundamentados no contexto correto. Agentes que conseguem recuperar e aplicar as informações mais relevantes no momento certo têm muito mais probabilidade de produzir resultados precisos e confiáveis.</p><p>Neste blog, usaremos <a href="https://mastra.ai/">o Mastra</a> para construir um agente de conhecimento que memoriza o que os usuários dizem e consegue recuperar informações relevantes posteriormente, utilizando o Elasticsearch como backend de memória e recuperação. Você pode facilmente estender esse mesmo conceito a casos de uso do mundo real, como agentes de suporte que conseguem se lembrar de conversas e soluções anteriores, permitindo que eles personalizem as respostas para usuários específicos ou apresentem soluções mais rapidamente com base no contexto prévio.</p><p>Acompanhe aqui como construir isso passo a passo. Se você se perder ou simplesmente quiser executar um exemplo finalizado, confira o repositório <a href="https://github.com/jdarmada/getting-started-mastra-elastic/tree/main">aqui</a>.</p><h2>O que é Mastra?</h2><p>Mastra é um framework TypeScript de código aberto para a construção de agentes de IA com componentes intercambiáveis para raciocínio, memória e ferramentas. Seu recurso <a href="https://mastra.ai/docs/memory/semantic-recall">de recuperação semântica</a> permite que os agentes se lembrem e recuperem interações passadas, armazenando mensagens como representações vetoriais em um banco de dados vetorial. Isso permite que os agentes mantenham o contexto e a continuidade da conversa a longo prazo. O Elasticsearch é um excelente armazenamento de vetores para habilitar esse recurso, pois oferece suporte a buscas vetoriais densas e eficientes. Quando a recuperação semântica é acionada, o agente extrai mensagens relevantes do passado para a janela de contexto do modelo, permitindo que o modelo use esse contexto recuperado como base para seu raciocínio e respostas.</p><h2>O que você precisa para começar</h2><ul><li><p>Node v18+</p></li><li><p>Elasticsearch (versão 8.15 ou mais recente)</p></li><li><p>Chave da API do Elasticsearch</p></li><li><p><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">Chave da API OpenAI</a></p></li></ul><p>Observação: você precisará disso porque a demonstração usa o provedor OpenAI, mas o Mastra é compatível com outros SDKs de IA e provedores de modelos da comunidade, então você pode facilmente trocá-lo dependendo da sua configuração.</p><h2>Construindo um projeto Mastra</h2><p>Usaremos a CLI integrada do Mastra para fornecer a estrutura básica do nosso projeto. Execute o comando:</p>npm create mastra@latest<p>Você receberá uma série de instruções, começando com:</p><p>1. Dê um nome ao seu projeto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87f941f654d03827/6a16f7af67045b214d45bfa1/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" alt="Como nomear um prompt no aplicativo Mastra" /><p>2. Podemos manter esta opção padrão; fique à vontade para deixar este campo em branco.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbb3d4f27435cac/6a16f7b0cdacbf29497d27de/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" alt="Indicar à Mastra onde guardar os arquivos de prompts." /><p>3. Para este projeto, usaremos um modelo fornecido pela OpenAI.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f654f6cb9397e94/6a16f7b2964cea899a08b942/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" alt="Selecionando um modelo fornecido pela OpenAI no Mastra" /><p>4. Selecione a opção “Ignorar por enquanto”, pois armazenaremos todas as nossas variáveis de ambiente em um arquivo `.env` que configuraremos em uma etapa posterior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff106117521a3519/6a16f7b3c1e8a5031af880d8/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" alt="Por enquanto, vou optar por ignorar a chave da OpenAI." /><p>5. Também podemos ignorar esta opção.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda7d9c51c3878d7/6a16f7b450916809dbe1b892/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" alt="" /><p>Assim que a inicialização estiver concluída, podemos passar para a próxima etapa.</p><h3>Instalando dependências</h3><p>Em seguida, precisamos instalar algumas dependências:</p>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv<ul><li><p><code>ai</code> - Pacote Core AI SDK que fornece ferramentas para gerenciar modelos de IA, prompts e fluxos de trabalho em JavaScript/TypeScript. O Mastra é construído sobre o <a href="https://ai-sdk.dev/">SDK de IA</a> da Vercel, portanto, precisamos dessa dependência para permitir as interações do modelo com o seu agente.</p></li><li><p><code>@ai-sdk/openai</code> - Plugin que conecta o SDK de IA aos modelos da OpenAI (como GPT-4, GPT-4o, etc.), permitindo chamadas à API usando sua chave de API da OpenAI.</p></li><li><p><code>@elastic/elasticsearch</code> - <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript">Cliente oficial do Elasticsearch para Node.js</a>, Utilizado para conectar-se ao seu Elastic Cloud ou cluster local para indexação, pesquisa e operações vetoriais.</p></li><li><p><code>dotenv</code> - Carrega variáveis de ambiente de um arquivo .env arquivo em process.env, permitindo que você insira credenciais com segurança, como chaves de API e endpoints do Elasticsearch.</p></li></ul><h3>Configurando variáveis de ambiente</h3><p>Crie um arquivo <code>.env</code> no diretório raiz do seu projeto, caso ainda não exista um. Alternativamente, você pode copiar e renomear o exemplo <code>.env</code> que eu forneci no <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/.env.example">repositório</a>. Neste arquivo, podemos adicionar as seguintes variáveis:</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>Isso conclui a configuração básica. A partir daqui, você já pode começar a construir e orquestrar agentes. Vamos dar um passo além e adicionar o Elasticsearch como camada de armazenamento e busca vetorial.</p><h2>Adicionando o Elasticsearch como armazenamento vetorial.</h2><p>Crie uma nova pasta chamada <code>stores</code> e, dentro dela, adicione este <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/src/mastra/stores/elastic-store.ts">arquivo</a>. Antes que a Mastra e a Elastic lancem uma integração oficial de armazenamento vetorial do Elasticsearch, <a href="https://github.com/abhiaiyer91">Abhi Aiyer</a>(CTO da Mastra) compartilhou esta classe protótipo inicial chamada <code>ElasticVector</code>. Em termos simples, ele conecta a abstração de memória do Mastra aos recursos de vetores densos do Elasticsearch, permitindo que os desenvolvedores utilizem o Elasticsearch como banco de dados de vetores para seus agentes.</p><p>Vamos analisar mais detalhadamente as partes importantes da integração:</p><h3>Ingestão do cliente Elasticsearch</h3><p>Esta seção define a classe <code>ElasticVector</code> e configura a conexão do cliente Elasticsearch com suporte para implantações padrão e sem servidor.</p>export interface ElasticVectorConfig extends ClientOptions {
    /**
     * Explicitly specify if connecting to Elasticsearch Serverless.
     * If not provided, will be auto-detected on first use.
     */
    isServerless?: boolean;
    
    /**
     * Maximum documents to count accurately when describing indices.
     * Higher values provide accurate counts but may impact performance on large indices.
     * 
     * @default 10000
     */
    maxCountAccuracy?: number;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

dotenv.config();

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

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

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

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

        vector: vectorStore,

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

        //set semantic recall options
        options: {
            semanticRecall: {
                topK: 3, // retrieve 3 similar messages
                messageRange: 2, // include 2 messages before/after each match
                scope: 'resource',
            },
        },
    }),
});<p>Os campos que podemos definir são:</p><ul><li><p><code>name</code> e <code>instructions</code>: Dê a ele uma identidade e uma função primária.</p></li><li><p><code>model</code>Estamos usando o <code>gpt-4o</code> da OpenAI por meio do pacote <code>@ai-sdk/openai</code> .</p></li><li><p><code>memory</code>:</p><ul><li><p><code>vector</code>: Aponta para o nosso armazenamento Elasticsearch, de onde os embeddings são armazenados e recuperados.</p></li><li><p><code>embedder</code>Qual modelo usar para gerar embeddings?</p></li><li><p><code>semanticRecall</code> As opções definem como funciona o recall:</p><ul><li><p><code>topK</code>Quantas mensagens semanticamente semelhantes devem ser recuperadas?</p></li><li><p><code>messageRange</code>: Qual a extensão da conversa a ser incluída em cada interação?</p></li><li><p><code>scope</code>Define o limite da memória.</p></li></ul></li></ul></li></ul><p>Quase pronto. Basta adicionarmos esse agente recém-criado à nossa configuração do Mastra. No arquivo chamado <a href="http://index.ts/"><code>index.ts</code></a>, importe o agente de conhecimento e insira-o no campo <code>agents</code> .</p>export const mastra = new Mastra({
  agents: { knowledgeAgent },
  storage: new LibSQLStore({
    // stores observability, scores, ... into memory storage, if it needs to persist, change to file:../mastra.db
    url: ":memory:",
  }),
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
  telemetry: {
    // Telemetry is deprecated and will be removed in the Nov 4th release
    enabled: false, 
  },
  observability: {
    // Enables DefaultExporter and CloudExporter for AI tracing
    default: { enabled: true }, 
  },
});<p>Os outros campos incluem:</p><ul><li><p><code>storage</code>Este é o repositório de dados interno do Mastra para histórico de execuções, métricas de observabilidade, pontuações e caches. Para obter mais informações sobre o sistema de armazenamento Mastra, visite <a href="https://mastra.ai/docs/server-db/storage">aqui</a>.</p></li><li><p><code>logger</code>O Mastra utiliza <a href="https://github.com/pinojs/pino">o Pino</a>, que é um registrador JSON estruturado e leve. Ele registra eventos como início e término de agentes, chamadas e resultados de ferramentas, erros e tempos de resposta do LLM.</p></li><li><p><code>observability</code>Controla o rastreamento de IA e a visibilidade da execução de agentes. Ele rastreia:</p><ul><li><p>Início/fim de cada etapa de raciocínio.</p></li><li><p>Qual modelo ou ferramenta foi utilizada?</p></li><li><p>Entradas e saídas.</p></li><li><p>Pontuações e avaliações</p></li></ul></li></ul><h3>Testando o agente com o Mastra Studio</h3><p>Parabéns! Se você chegou até aqui, está pronto para executar este agente e testar suas capacidades de recuperação semântica. Felizmente, o Mastra oferece uma interface de chat integrada, então não precisamos criar a nossa própria.</p><p>Para iniciar o servidor de desenvolvimento do Mastra, abra um terminal e execute o seguinte comando:</p>npm run dev<p>Após a inicialização e o empacotamento iniciais do servidor, você deverá receber um endereço para o Playground.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5f857fddc74ffc9/6a16f7b6a6c2b995d5e794c0/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" alt="Endereço do servidor para Playground" /><p>Cole este endereço no seu navegador e você será direcionado para o Mastra Studio.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fdda6ce46ce068/6a16f7b7b0367d4f7672bacf/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" alt="Colar o endereço do Playground para acessar o Mastra Studio" /><p>Selecione a opção <code>knowledgeAgent</code> e comece a conversar.</p><p>Para um teste rápido para verificar se tudo está conectado corretamente, forneça algumas informações como: "A equipe anunciou que o desempenho de vendas em outubro aumentou 12%, impulsionado principalmente por renovações de contratos corporativos." O próximo passo é expandir o alcance aos clientes de médio porte.” Em seguida, inicie um novo bate-papo e faça uma pergunta como: "Em qual segmento de clientes dissemos que precisamos nos concentrar a seguir?" O agente de conhecimento deve ser capaz de recordar as informações que você lhe forneceu na primeira conversa. Você deverá ver uma resposta semelhante a esta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfec3266e81a7213b/6a16f7b92b835f6f70f4afe2/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" alt="Ao conversar com um agente de conhecimento no Mastra Studio, o agente consegue recuperar informações." /><p>Ao recebermos uma resposta como essa, significa que o agente armazenou com sucesso nossa mensagem anterior como embeddings no Elasticsearch e a recuperou posteriormente usando a busca vetorial.</p><h3>Inspecionando o armazenamento de memória de longo prazo do agente.</h3><p>Acesse a aba <code>memory</code> na configuração do seu agente no Mastra Studio. Isso permite que você veja o que seu agente aprendeu ao longo do tempo. Cada mensagem, resposta e interação que é incorporada e armazenada no Elasticsearch passa a fazer parte dessa memória de longo prazo. Você pode realizar buscas semânticas em interações passadas para encontrar rapidamente informações ou contextos que o agente aprendeu anteriormente. Este é essencialmente o mesmo mecanismo que o agente usa durante a recuperação semântica, mas aqui você pode inspecioná-lo diretamente. No exemplo abaixo, estamos pesquisando o termo "vendas" e obtendo como resultado todas as interações que incluíram algo relacionado a vendas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte428134d7bf2a43a/6a16f7bbb0367d185872bad3/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" alt="Como inspecionar o armazenamento de memória de longo prazo dos agentes de conhecimento" /><h2>Conclusão</h2><p>Ao conectar o Mastra e o Elasticsearch, podemos fornecer memória aos nossos agentes, o que é uma camada fundamental na engenharia de contexto. Com a recuperação semântica, os agentes podem construir contexto ao longo do tempo, fundamentando suas respostas no que aprenderam. Isso significa interações mais precisas, confiáveis e naturais.</p><p>Essa integração inicial é apenas o ponto de partida. O mesmo padrão pode permitir que agentes de suporte se lembrem de chamados anteriores, bots internos recuperem documentação relevante ou assistentes de IA consigam recordar detalhes do cliente no meio da conversa. Também estamos trabalhando para uma integração oficial com o Mastra, tornando essa combinação ainda mais perfeita em um futuro próximo.</p><p>Estamos ansiosos para ver o que você vai construir em seguida. Experimente, explore <a href="https://mastra.ai/">o Mastra</a> e seus recursos de memória e sinta-se à vontade para compartilhar suas descobertas com a comunidade.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
    <category><![CDATA[IA agêntica]]></category>
    <category><![CDATA[Experiência do Desenvolvedor]]></category>
    <category><![CDATA[Integrações]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09afdbff05603865/6a16f7bd839dfabbf2dcfcb5/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Como exibir os campos de um índice do Elasticsearch]]></title>
    <description><![CDATA[Aprenda como exibir os campos de um índice do Elasticsearch usando as APIs _mapping e _search, subcampos, _source sintético e campos de tempo de execução.]]></description>
    <content:encoded><![CDATA[<p>Neste artigo, discutiremos como exibir os campos de um índice do Elasticsearch. Isso pode ser útil para entender a estrutura dos seus dados, identificar campos específicos e solucionar problemas. Abordaremos os seguintes tópicos:</p><ol><li><p>Utilizando a API <code>_mapping</code> para recuperar informações de campo</p></li><li><p>Utilizando a API <code>_search</code> para exibir valores de campo</p></li><li><p>Exibição de subcampos</p></li><li><p>_source sintética</p></li><li><p>Campos de tempo de execução</p></li></ol><h2>1. Utilizando a API _mapping para recuperar informações de campo</h2><p>A API <code>_mapping</code> permite recuperar a definição de mapeamento para um índice ou vários índices. Isso inclui informações sobre os campos, seus tipos de dados e outras propriedades. Para recuperar o mapeamento de um índice específico, utilize a seguinte solicitação:</p>GET /&lt;index_name&gt;/_mapping<p>Por exemplo, se você tiver um índice chamado <code>my_index</code>, poderá recuperar seu mapeamento com a seguinte solicitação:</p>GET /my_index/_mapping<p>A resposta incluirá a definição de mapeamento para o índice, que contém informações sobre os campos e suas propriedades.</p><p>Também é possível recuperar o mapeamento de um campo específico. Isso pode ser útil se o seu mapeamento for muito extenso e você quiser se concentrar apenas em um campo específico. Para obter o mapeamento de um campo específico, utilize a seguinte solicitação:</p>GET /my_index/_mapping/field/my_field<p>Você também pode recuperar os mapeamentos de vários campos separando seus nomes por vírgulas, como na seguinte solicitação:</p>GET /my_index/_mapping/field/my_field_1,my_field_2,my_field_3<h2>2. Usando a API _search para exibir valores de campo</h2><p>Para exibir os valores dos campos em um índice do Elasticsearch, você pode usar a API <code>_search</code> . A API <code>_search</code> oferece várias maneiras de controlar quais campos são retornados; as duas principais são:</p><ol><li><p><strong><code>_source</code></strong>O campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field"><code>_source</code></a> contém o corpo original do documento JSON exatamente como foi indexado, incluindo quaisquer alterações feitas pelos pipelines de ingestão ou etapas de pré-processamento. Para exibir campos específicos do documento de origem, implemente a filtragem de origem, como veremos a seguir.</p></li><li><p><strong><code>fields</code></strong>O parâmetro <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields"><code>fields</code></a> permite recuperar campos específicos dos seus documentos ao realizar uma pesquisa, com base no mapeamento do índice. Ao contrário de <code>_source</code>, <code>fields</code> também pode retornar valores de campos armazenados, valores de documentos ou campos de tempo de execução sem fazer referência a <code>_source</code>, embora para campos padrão sem valores de documentos ou configurações armazenadas, ele recorra a <code>_source</code>. Isso pode trazer muitos benefícios, como melhoria de desempenho e outros, como veremos a seguir.</p></li></ol><h3>Usando o campo _source</h3><p>Por padrão, a API<code> _search</code> retorna o campo <code>_source</code> , que contém o documento JSON original que foi indexado. Para exibir campos específicos, você pode adicionar filtros no parâmetro <code>_source </code>da solicitação de pesquisa; isso é chamado de filtragem de origem.</p><p>Aqui está um exemplo de uma solicitação de pesquisa que retorna os valores dos campos <code>title </code>e <code>author</code> para documentos no índice <code>my_index</code> :</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["title", "author"]
}<p>Neste exemplo, o parâmetro <code>_source</code> especifica os campos a serem retornados.</p><p>Se você precisar de ainda mais controle, pode usar as propriedades <code>includes</code> e <code>excludes </code>do objeto <code>_source</code> . Por exemplo, a consulta abaixo retorna o campo de nível superior <code>title</code> e todos os subcampos de <code>author</code> exceto <code>author.description</code>.</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "_source": {
     “includes”: [“title”, “author.*],
     “excludes”: [“author.description”]
  }
}<p>Neste exemplo, usamos o padrão <code>author.* </code>para recuperar todos os subcampos diretos do objeto <code>author </code> . Então excluímos explicitamente <code>author.description </code>para que apenas os outros campos de autor sejam retornados. Note que isso não traz nenhuma melhoria de desempenho, já que ainda precisa carregar e analisar o JSON de origem, mas pode reduzir o tamanho da resposta enviada pela rede.</p><h3>Usando o parâmetro de campos</h3><p>Você pode usar o parâmetro <code>fields</code> para filtrar os campos retornados na resposta da pesquisa. O uso de <code>fields</code> em vez de <code>_source</code> oferece diversas vantagens, incluindo:</p><ul><li><p><strong>Desempenho aprimorado: </strong><code>fields </code>pode retornar valores diretamente de <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">campos armazenados</a> ou <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/doc-values">valores de documentos</a> sem ter que carregar o <code>_source</code> completo, tornando o tamanho da carga útil da resposta menor.</p></li><li><p><strong>Saída formatada:</strong> Para campos padrão,<code> fields</code> pode recorrer a <code>_source</code> para obter os valores, mas ele analisa o mapeamento do índice para formatar corretamente a saída, como datas formatadas, tornando-as consistentes com o que é usado para agregações e classificação.</p></li><li><p><strong>Acesso a campos de tempo de execução:</strong> <code>fields</code> pode retornar campos de tempo de execução, que não existem no <code>_source</code> original.</p></li><li><p>Você pode encontrar mais benefícios <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#search-fields-param">aqui</a>.</p></li></ul><p>Por exemplo, para retornar apenas os campos <code>title</code> e <code>author</code> no índice <code>my_index</code> , você pode usar a seguinte solicitação de pesquisa:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author"],
  "_source": false
}<p>Na consulta acima, definimos o campo <code>_source </code>como falso para não retornarmos o documento de origem. Isso pode minimizar drasticamente o tamanho da carga útil da resposta, mas lembre-se de que isso só funciona porque os campos <code>title</code> e <code>author</code> são do tipo de campo <code>keyword </code> , que têm <code>doc_values</code> habilitado por padrão. Se o campo não tiver <code>doc_values</code> habilitado e <code>_source</code> estiver definido como falso, o Elasticsearch não terá como recuperá-los e eles serão ignorados na resposta.</p><p>É importante notar que a resposta <code>fields</code> sempre retorna uma matriz de valores para cada campo, mesmo que haja apenas um único valor. Isso ocorre porque o Elasticsearch não possui um tipo de array dedicado, e qualquer campo pode ter vários valores. Para obter mais informações sobre arrays no Elasticsearch, clique <a href="http://elastic.co/docs/reference/elasticsearch/mapping-reference/array">aqui</a>.</p><h3>Outras formas de recuperar campos</h3><p>Embora a recuperação de campos usando <code>_source</code> ou <code>fields</code> sejam os métodos recomendados, existem outros métodos disponíveis para casos de uso específicos, como:</p><p><strong>Campos de valor do documento:</strong> Se você quiser evitar <code>_source</code> completamente, você pode pesquisar usando o parâmetro <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/retrieve-selected-fields#docvalue-fields"><code>docvalue_fields</code></a> . Os valores do documento armazenam os mesmos valores de campo que <code>_source</code> , mas em uma estrutura de dados em disco, otimizada para classificação e agregações.</p><p>Como é separado dos valores armazenados com <code>_source</code>, você pode solicitar campos específicos sem carregar todo o <code>_source</code>. Isso é útil se você estiver consultando documentos grandes, mas precisar apenas de alguns campos pequenos que suportem valores do tipo "doc". Outro caso de uso para usar <code>docvalue_fields </code>é quando você deseja usar formatação personalizada nos campos <code>date</code> e <code>numeric</code> , como veremos no exemplo abaixo.</p><p>Observe que isso só funciona para campos que você habilita <code>doc_values</code> ou para tipos de campo que o têm habilitado por padrão, como <code>keyword</code>, <code>date</code>, tipos numéricos e <code>boolean</code>, não para <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/text"><code>text</code></a> ou <a href="https://www.elastic.co/docs/reference/elasticsearch/plugins/mapper-annotated-text-usage"><code>annotated_text</code></a>.</p><p>Neste exemplo, usamos o parâmetro <code>docvalue_fields</code> para recuperar os campos <code>title</code>, <code>author</code> e <code>published</code> sem carregar o documento <code>_source</code> completo:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "docvalue_fields": [
    "title",
    "author",
    {
      "field": "published",
      "format": "epoch_millis"
    }
  ],
  "_source": false
}<p>Quando esta consulta é executada, o Elasticsearch obtém os valores diretamente de seu armazenamento colunar em disco, em vez de referenciar o <code>_source </code>para cada documento. O campo <code>published</code> é retornado com o formato <code>epoch_millis</code> em vez do formato padrão, graças ao parâmetro <code>format</code> fornecido na consulta.</p><p><strong>Campos armazenados:</strong> Se você marcou explicitamente campos específicos como <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-store">armazenados</a> no mapeamento, você pode usar o parâmetro <code>stored_fields</code> para filtrar esses campos. Isso é útil se você deseja respostas resumidas apenas com esses campos específicos ou para campos que você armazenou deliberadamente para recuperação posterior. É armazenado separadamente de <code>_source</code>, portanto, este método também é útil para evitar a necessidade de carregar <code>_source</code>.</p><p>É importante notar que esta opção está desativada por padrão e geralmente não é recomendada. Em vez disso, utilize a filtragem de origem para retornar determinados subconjuntos do documento de origem original.</p><p>Na consulta de exemplo abaixo, usamos o parâmetro <code>stored_fields</code> para recuperar o campo <code>summary</code> , que tem a configuração de mapeamento de índice de ”<code>store”: true</code>.</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "stored_fields": ["summary"]
}<p>Quando esta consulta é executada, o Elasticsearch verifica se este campo foi marcado com <code>”store”: true</code>, se não o encontrar, irá ignorar o campo completamente.</p><h2>3. Exibição de subcampos</h2><p>Se o seu índice contiver subcampos, você pode usar a notação de ponto para especificar o caminho do campo no parâmetro <code>fields</code> . Note que os subcampos são diferentes do <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/nested">tipo de campo aninhado</a>. Por exemplo, se você tiver um subcampo chamado <code>address.city</code>, poderá incluí-lo na resposta da pesquisa desta forma:</p>GET /my_index/_search
{
  "query": {
    "match_all": {}
  },
  "fields": ["title", "author", "address.city"],
  "_source": false
}<p>Neste exemplo, a resposta da pesquisa incluirá os valores dos campos <code>title</code>, <code>author</code> e <code>address.city</code> .</p><h2>4. Fonte sintética</h2><p>Se você quiser manter a funcionalidade de usar<code> _source</code> , mas também economizar espaço em disco, você tem a opção de usar <code>_source</code> sintético em seu mapeamento de índice. <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source"><code>_source</code></a> <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-source-field#synthetic-source">sintético </a>é um recurso que permite ao Elasticsearch reconstruir o <code>_source</code> a partir de dados existentes, como campos armazenados e valores de documentos, mesmo quando <code>_source</code> está desativado. Isso permite economizar bastante espaço de armazenamento, ao custo de velocidades ligeiramente menores no momento da consulta, já que a reconstrução ocorre em tempo real. Ative este recurso usando os valores abaixo nas configurações do seu índice:</p>PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}<p>Algumas vantagens de usar <code>_source </code>sintético incluem: exibição do documento completo ao usar a API <code>_search</code> , filtragem de origem e compatibilidade com outros recursos e ferramentas como o Kibana que esperam que <code>_source</code> esteja disponível, tudo isso evitando a necessidade de armazenar o documento <code>_source</code> completo.</p><h2>5. Campos de tempo de execução</h2><p><a href="https://www.elastic.co/docs/manage-data/data-store/mapping/runtime-fields">Os campos de tempo de execução</a> permitem definir campos com script no momento da consulta ou no mapeamento do índice, dentro de um bloco de tempo de execução. Esses campos nunca são indexados, portanto, adicionar um campo de tempo de execução não aumenta o tamanho do índice, mas nunca aparecerá em <code>_source</code>. Os campos de tempo de execução definidos no mapeamento são persistentes e estão disponíveis para todas as consultas, enquanto os campos de tempo de execução definidos no momento da consulta são temporários e estão disponíveis apenas nessa solicitação de pesquisa.</p><p>A principal vantagem de usar campos em tempo de execução é a capacidade de adicionar campos aos documentos depois de já os ter importado, simplificando as decisões de mapeamento. Os campos de tempo de execução também são ótimos para enriquecer seus documentos com valores que não existem no documento original, mas são gerados por meio de um script, como formatar uma string ou calcular uma pontuação.</p><p>Vale ressaltar também que os campos de tempo de execução podem prejudicar o desempenho, pois será necessário executar um script para cada documento no conjunto de resultados. Para <a href="https://www.elastic.co/docs/manage-data/data-store/mapping/retrieve-runtime-field">recuperar um campo de tempo de execução</a>, você também pode usar o parâmetro <code>fields</code> na API <code>_search</code> .</p><h2>Conclusão</h2><p>A exibição de campos de um índice Elasticsearch pode variar desde a simples recuperação de valores usando o mapeamento de índice ou o <code>_source</code>, até métodos mais avançados usando <code>fields</code>, <code>docvalue_fields</code> ou campos de tempo de execução para maior controle e eficiência. Compreender as vantagens e desvantagens de diferentes métodos é fundamental para otimizar suas experiências de busca. Seja para otimizar payloads, enriquecer documentos ou usar dados sintéticos <code>_source</code> para economizar armazenamento, o Elasticsearch oferece diversas ferramentas e recursos para encontrar os dados que você precisa, da maneira que você precisa. Essas técnicas podem ajudá-lo a entender a estrutura de seus dados, identificar campos específicos e solucionar problemas.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-index-show-fields</guid>
    <category><![CDATA[Dados de indexação]]></category>
    <category><![CDATA[Mapeamentos]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd041e871a8935448/6a17de320b0bedf404dd34ab/23b96aaa1a38b1f4747b4a87695d816f24c0cf70-720x421.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 06 Aug 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[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>
  </channel>
</rss>