12 ‐ Search e GenAI - BktechBrazil/elastic-eck GitHub Wiki

12.1 - Elasticsearch como plataforma de Search AI

O Elastic Stack é organizado em torno de quatro soluções: Observabilidade, Segurança, Busca e a infraestrutura que sustenta todas elas. Este capítulo cobre o pilar de Busca com foco nas funcionalidades de inteligência artificial nativas do Elasticsearch 9.x.

A evolução da busca no Elasticsearch seguiu uma trajetória clara, e cada camada resolveu um problema real sem substituir a anterior:

1. Busca por palavras-chave (BM25)
         ↓  resolve: busca relevante por termos exatos
2. Busca semântica (ELSER / E5)
         ↓  resolve: busca por significado, não por palavras exatas
3. Busca híbrida (BM25 + semântica com RRF)
         ↓  resolve: combina precisão lexical com entendimento semântico
4. RAG — Retrieval Augmented Generation
         ↓  resolve: respostas em linguagem natural fundamentadas nos seus dados
5. Agentes de IA com acesso a ferramentas
            resolve: automação inteligente de fluxos de trabalho

12.2 - Fundamentos técnicos da busca com IA

12.2.1 - BM25: a base que ainda importa

O BM25 (Best Match 25) é o algoritmo de ranking padrão do Elasticsearch desde a versão 5.x. Calcula relevância com base em dois fatores:

TF (Term Frequency) com saturação: quanto mais vezes um termo aparece no documento, maior o score — mas com saturação. A décima ocorrência da palavra não vale dez vezes a primeira. Isso evita que documentos com repetição artificial de palavras-chave dominem os resultados.

IDF (Inverse Document Frequency): termos raros no índice valem mais do que termos comuns. Uma palavra que aparece em poucos documentos é discriminante. Uma palavra que aparece em todos os documentos não diz nada sobre relevância.

Quando o BM25 funciona muito bem:

  • Busca por termos técnicos precisos: siglas, nomes de produtos, IDs, versões de software
  • Usuários que conhecem a terminologia exata do domínio

Limitação fundamental: o BM25 compara tokens literalmente. Ele não entende que "timeout de conexão" e "falha de rede" descrevem o mesmo problema. Se o documento usa palavras diferentes das da query, o BM25 não encontra.

12.2.2 - Embeddings e modelos de linguagem

Para superar a limitação do BM25, entrou a busca vetorial. Modelos de linguagem transformam texto em vetores numéricos — listas de números de alta dimensão onde textos com significado parecido ficam próximos no espaço matemático:

"timeout de conexão"  → [0.23, -0.41, 0.87, 0.12, ...]
"falha de rede"       → [0.21, -0.39, 0.85, 0.11, ...]  ← próximos
"receita de bolo"     → [-0.92, 0.11, -0.34, 0.76, ...] ← distantes

A busca encontra documentos por proximidade geométrica entre vetores — independentemente das palavras usadas.

Existem dois tipos principais de representação vetorial:

Dense vectors (vetores densos): todas as dimensões têm valor. Modelos como o E5 produzem vetores com centenas de dimensões onde cada número carrega significado contextual. A similaridade é calculada por distância cosseno ou produto escalar entre os vetores.

Sparse vectors (vetores esparsos): a maioria dos valores é zero — apenas os termos relevantes têm peso. O ELSER usa essa abordagem. É mais eficiente em memória e mais explicável — você consegue ver quais termos foram usados para determinar a relevância.

12.2.3 - ELSER: busca semântica para inglês

O ELSER (Elastic Learned Sparse EncodeR) é o modelo de busca semântica desenvolvido pela Elastic. Usa sparse vectors: em vez de produzir um vetor denso, expande o texto em um dicionário de termos com pesos de relevância:

Texto: "Como diagnosticar lentidão em uma aplicação web"
 
Representação ELSER:
{
  "latência":       0.89,
  "performance":    0.84,
  "throughput":     0.78,
  "gargalo":        0.71,
  "monitoramento":  0.68,
  "profiling":      0.62,
  ...
}

Esses termos não são sinônimos — são associações aprendidas pelo modelo a partir de bilhões de textos de treinamento. O modelo aprendeu que textos sobre "lentidão em aplicação web" co-ocorrem com "latência", "throughput", "profiling" — mesmo que essas palavras não estejam no texto original.

Características:

  • Idioma: otimizado para inglês. Para outros idiomas, use o E5 multilingual.
  • Sem fine-tuning: funciona out-of-the-box em qualquer domínio
  • Eficiente: a busca por interseção de sparse vectors é similar ao índice invertido do BM25 — mais rápida que calcular distância entre dense vectors
  • Endpoints: .elser-2-elastic (no Elastic Inference Service, via Cloud Connect) ou .elser-2-elasticsearch (em um node de ML do próprio cluster)

12.2.4 - E5 Multilingual: busca semântica para outros idiomas

O E5 (Multilingual E5 Small) é o modelo de embedding multilingual recomendado pela Elastic para textos em idiomas diferentes do inglês — incluindo português.

Ao contrário do ELSER que usa sparse vectors, o E5 usa dense vectors de 384 dimensões. Ele foi treinado em dados de 100+ idiomas e consegue capturar o significado semântico em português, espanhol, francês, alemão, japonês e muitos outros.

Como funciona a busca com E5:

O E5 transforma tanto o documento quanto a query em vetores densos. A busca encontra documentos cujos vetores são geometricamente próximos do vetor da query — usando o algoritmo HNSW (Hierarchical Navigable Small World) para busca aproximada eficiente.

Quando usar E5 em vez de ELSER:

  • Documentos e queries em português ou qualquer idioma não-inglês
  • Ambientes multilinguais onde documentos em vários idiomas coexistem no mesmo índice
  • Quando a qualidade semântica em português é prioridade

Endpoints disponíveis:

  • No Elastic Inference Service (via Cloud Connect): .multilingual-e5-small-elastic ⚠️ VALIDAR: confirmar no GET /_inference do laboratório se o endpoint está disponível no EIS com esse nome na 9.4
  • Em um node de ML do próprio cluster: .multilingual-e5-small-elasticsearch

Importante sobre o tipo de embedding: O ELSER gera sparse_embedding — o resultado é um dicionário de termos com pesos. O E5 gera text_embedding — o resultado é um vetor denso de 384 números.

O campo semantic_text abstrai essa diferença: independente do modelo configurado no inference_id, você usa a mesma query semantic e o Elasticsearch executa o tipo correto de busca automaticamente.

12.2.5 - semantic_text: o campo que abstrai tudo

O tipo semantic_text (GA no Elasticsearch 9.x) simplifica radicalmente a configuração de busca semântica. Você declara o campo com o tipo e o inference_id do modelo:

"conteudo_semantico": {
  "type": "semantic_text",
  "inference_id": ".multilingual-e5-small-elastic"
}

O Elasticsearch cuida de:

  • Gerar os embeddings automaticamente quando um documento é indexado
  • Aplicar chunking em textos longos (divisão em partes menores que o modelo consegue processar)
  • Executar o tipo correto de busca (sparse ou dense) quando você usa a query semantic

Boa prática de modelagem: mantenha um campo text com o mesmo conteúdo usando copy_to. O campo text serve para busca BM25 e recuperação do texto original. O semantic_text serve para busca semântica. Com copy_to, você indexa apenas no campo text e os dois ficam sincronizados automaticamente.

"conteudo": {
  "type": "text",
  "copy_to": "conteudo_semantico"
},
"conteudo_semantico": {
  "type": "semantic_text",
  "inference_id": ".multilingual-e5-small-elastic"
}

12.2.6 - Busca Híbrida com RRF

A busca híbrida combina BM25 e busca semântica usando RRF (Reciprocal Rank Fusion). O RRF pondera os resultados pela posição em cada lista de ranking:

RRF_score(doc) = Σ 1 / (rank_constant + posição_em_cada_lista)

Onde rank_constant (padrão: 60) evita que documentos no topo de uma única lista dominem. Um documento bem ranqueado em ambas as listas acumula mais pontos do que um que aparece bem em apenas uma.

Na prática com semantic_text: o segundo retriever usa match no campo semantic_text — o Elasticsearch detecta automaticamente o tipo do campo e executa busca semântica (não BM25), preservando a sintaxe simples.

Por que usar busca híbrida:

  • BM25 é preciso para termos técnicos exatos (siglas, IDs, nomes de produtos)
  • Busca semântica é melhor para linguagem natural e conceitos
  • O RRF combina os pontos fortes de cada abordagem

12.2.7 - RAG: Retrieval Augmented Generation

RAG combina busca com um modelo de linguagem para responder perguntas com base nos seus dados reais. O problema que resolve: LLMs são treinados até uma certa data e não têm acesso a dados privados — isso gera alucinações (respostas plausíveis mas incorretas).

O fluxo:

Pergunta do usuário
        ↓
Elasticsearch busca os documentos mais relevantes
(busca semântica, BM25 ou híbrida)
        ↓
Documentos recuperados + Pergunta → LLM
        ↓
Resposta em linguagem natural baseada nos seus dados
(com citações dos documentos usados)

Por que não enviar todos os documentos ao LLM?

  • Limite de contexto: os LLMs aceitam uma quantidade limitada de tokens — impossível enviar uma base de conhecimento inteira
  • Custo: APIs de LLM cobram por token processado
  • O Elasticsearch recupera apenas os documentos mais relevantes como contexto

O RAG é o que o AI Assistant, o Agent Builder e o Playground implementam internamente.

12.2.8 - Agentes: Agent Builder

O Agent Builder (GA desde a 9.3) cria agentes de IA que respondem e agem sobre os dados em linguagem natural. Um agente recebe uma pergunta e decide quais tools usar — por exemplo, uma busca no índice de produtos e outra nos pedidos —, junta os resultados e redige a resposta com o LLM. Um agente combina:

  • instruções (o system prompt, que define papel, escopo e formato das respostas);
  • tools, que consultam os índices do Elasticsearch;
  • um LLM, configurado por um conector.

Na 9.4, o Playground foi descontinuado (deprecated) em favor do Agent Builder.

12.2.9 - Cloud Connect e Elastic Inference Service

O Cloud Connect conecta um cluster Elasticsearch self-managed aos serviços da Elastic Cloud sem mover dados ou alterar a arquitetura existente. Ele funciona também com clusters ECK.

O EIS (Elastic Inference Service) é o serviço de inferência que roda na infraestrutura de GPU da Elastic Cloud. Com a conexão estabelecida:

  • O processamento de inferência (geração de embeddings, execução de LLMs) acontece nos GPUs da Elastic
  • Os dados indexados permanecem no cluster local — nada é migrado
  • O Elasticsearch cria automaticamente os endpoints de inferência para os modelos de embedding e de reranking, e o Kibana cria os conectores dos LLMs gerenciados (Elastic Managed LLMs)

Requisitos:

  • licença Enterprise ou trial ativa no cluster;
  • uma conta na Elastic Cloud com trial ativa ou forma de pagamento configurada. O EIS é cobrado por milhão de tokens processados;
  • acesso de saída à internet a partir do Elasticsearch e do Kibana.

12.2.10 - Alternativa sem nuvem: modelos no próprio cluster

Quando o cluster não pode acessar a Elastic Cloud, tudo pode rodar localmente:

  • Embeddings: os endpoints .elser-2-elasticsearch e .multilingual-e5-small-elasticsearch rodam o modelo em um node de ML do próprio cluster. Na primeira chamada, o modelo é baixado e implantado automaticamente (o que leva alguns minutos), com alocações adaptativas que podem chegar a zero quando não há uso. O modelo consome a memória de ML do node, além do heap;
  • LLM: um LLM compatível com a API da OpenAI (Ollama, LM Studio, vLLM), rodando no cluster ou em outro servidor, conectado pelo conector OpenAI com o provedor Other (OpenAI Compatible Service).

No ECK, o LLM local pode rodar como um Deployment comum no mesmo cluster Kubernetes, como no Laboratório 6.


12.3 - Preparação dos laboratórios

Os laboratórios usam o cluster lab-es com um node, na versão 9.4.4, e o Kibana lab-kb.

Passo 1 — Conferir o cluster

cd ~/eck-lab
kubectl -n elastic get elasticsearch,kibana

Resultado esperado: lab-es e lab-kb com HEALTH green e VERSION 9.4.4.

Passo 2 — Acessar o Kibana

PASSWORD=$(kubectl -n elastic get secret lab-es-es-elastic-user \
  -o go-template='{{.data.elastic | base64decode}}')
echo "Senha do elastic: $PASSWORD"

pkill -f "port-forward.*5601" || true
nohup kubectl -n elastic port-forward --address 0.0.0.0 service/lab-kb-kb-http 5601 > /tmp/pf-kibana.log 2>&1 &
sleep 2
cat /tmp/pf-kibana.log

Resultado esperado: a senha do usuário elastic e a mensagem Forwarding from 0.0.0.0:5601 -> 5601.

O túnel fica ligado ao pod do Kibana que estava rodando quando ele foi aberto. Se esse pod for reiniciado ou recriado, o túnel cai e o Kibana deixa de responder no navegador: execute este bloco novamente.

Passo 3 — Conferir a licença

No Console:

GET /_license

Os recursos deste capítulo exigem licença trial ou Enterprise. Se o type for basic, ative a trial na página License Management (busca global), com Start trial. A trial do Elasticsearch só pode ser ativada uma vez e dura 30 dias.


12.4 - Laboratório 1: Cloud Connect e Elastic Inference Service

O EIS é cobrado por token processado. Com uma trial da Elastic Cloud, o consumo do laboratório é coberto pelos créditos da trial; ao final do capítulo, desconecte o serviço (seção 12.11).

Passo 1 — Criar conta na Elastic Cloud e gerar a Cloud Connect API key

  1. Acesse cloud.elastic.co
  2. Clique em Start free trial — sem necessidade de cartão de crédito
  3. Confirme o email e faça login no painel
  4. Clique no avatar no canto superior direito → Organization
  5. Clique na aba API keys → Create API key
  6. Nome: cloud-connect-treinamento → Create API key
  7. Copie e salve a key — exibida apenas uma vez

Não é necessário criar nenhum deployment na Cloud. Apenas a conta para gerar a API key.

Passo 2 — Conectar o cluster ao Cloud Connect

  1. No Kibana local, abra a barra de busca global (Ctrl+/)
  2. Pesquise por Cloud Connect
  3. Cole a API key no campo e clique em Connect

Passo 3 — Conectar o Elastic Inference Service

  1. Na tela de Cloud connected services, clique em Connect ao lado do Elastic Inference Service
  2. Aguarde alguns segundos até a confirmação

Passo 4 — Verificar os endpoints criados automaticamente

GET /_inference

A lista traz os endpoints do EIS (com o sufixo -elastic, como .elser-2-elastic) e os endpoints dos nodes de ML locais (com o sufixo -elasticsearch). Anote o endpoint do E5 multilingual no EIS, usado nos laboratórios seguintes: .multilingual-e5-small-elastic.

⚠️ VALIDAR: se o endpoint .multilingual-e5-small-elastic não aparecer na lista, use outro endpoint multilíngue de text_embedding do EIS exibido no GET /_inference (por exemplo, um modelo Jina Embeddings) e troque o inference_id nos mapeamentos dos laboratórios 4 e 5.

Passo 5 — Configurar o GenAI Settings e instalar a documentação

  1. Acesse a página GenAI Settings (em Stack Management, seção AI, ou pela busca global)
  2. Confirme que o LLM aparece como conectado (o aviso "No Large Language Model detected" desapareceu)
  3. Na seção Documentation, clique em Install nos dois pacotes:
    • Elastic documentation
    • Security labs

Esses pacotes alimentam o AI Assistant com a documentação oficial, tornando as respostas mais precisas e fundamentadas.


12.5 - Laboratório 2: AI Assistant

O AI Assistant é o assistente de IA integrado ao Kibana, acessível pelo botão do assistente no canto superior direito. Diferencia-se de um LLM genérico por:

  • Acesso ao cluster em tempo real: executa queries, verifica estado de índices e analisa dados durante a conversa
  • Base de conhecimento indexada: usa RAG com a documentação oficial instalada para fundamentar as respostas na documentação atual do produto

⚠️ VALIDAR: conferir no Kibana 9.4 o nome do botão do assistente no canto superior direito.

Parte 1 — Perguntas sobre o cluster

Pergunta 1:

Qual é o estado atual do meu cluster? Quantos nodes temos e quais são seus roles?

Pergunta 2:

Quais são os 5 maiores índices do cluster em termos de tamanho em disco?
Algum deles tem algum problema?

Pergunta 3:

Verifique se há algum índice com erro no ILM. Se houver, explique o que
pode ter causado e como resolver.

Parte 2 — Ajuda para criar queries

Pergunta 4:

Preciso de uma aggregation que mostre, no índice kibana_sample_data_ecommerce:
- A média de taxful_total_price por category.keyword
- O total de taxful_total_price por category.keyword
- A categoria com o maior valor médio
Gere a query completa e explique cada parte.

Copie a query gerada e execute no Console para verificar.

Parte 3 — Perguntas sobre documentação

Pergunta 5:

Qual é a diferença entre forcemerge e o processo de merge automático
do Elasticsearch? Quando devo usar cada um?

Pergunta 6:

Quando devo usar o tipo de campo nested em vez de object?
Me dê um exemplo prático de quando a diferença importa.

Pergunta 7:

Quais são as regras práticas de sizing de shards que a Elastic recomenda?

Parte 4 — Análise diagnóstica

Pergunta 8:

Faça uma análise completa do meu cluster e me diga:
1. O estado geral de saúde
2. Se há algum shard não alocado e por quê
3. Se o uso de heap está adequado
4. Quais índices podem precisar de atenção

12.6 - Laboratório 3: Agent Builder

O Agent Builder cria assistentes de IA customizados com comportamento específico, instruções definidas e acesso controlado a fontes de dados indexadas.

Diferença do AI Assistant: o AI Assistant é um assistente genérico do Elastic Stack. O Agent Builder permite criar assistentes especializados para casos de uso do seu negócio — com personalidade, escopo e base de conhecimento próprios.

Casos de uso reais:

  • Agente de suporte técnico com acesso à documentação interna indexada
  • Agente de análise que interpreta alertas do SIEM e sugere próximos passos
  • Agente de observabilidade que diagnostica problemas com dados de APM

Passo 1 — Acessar o Agent Builder

Abra Agent Builder pelo menu lateral ou pela busca global.

Passo 2 — Criar um novo agente

Clique em Create agent.

Passo 3 — Configurar as instruções (system prompt)

No campo Instructions, cole:

Você é ElasticBot, um especialista técnico em Elasticsearch e Elastic Stack
criado pela BKTECH — maior parceiro Elastic do Brasil.
 
Responda sempre em português brasileiro, de forma técnica e objetiva.
 
Você pode ajudar com:
- Explicar conceitos do Elasticsearch (mapping, shards, ILM, aggregations,
  analyzers, busca semântica)
- Criar e explicar queries, aggregations e configurações
- Diagnosticar problemas de performance e sugerir soluções
- Explicar erros e como resolvê-los
- Recomendar boas práticas de implementação e sizing
 
Diretrizes de comportamento:
- Sempre forneça exemplos de queries ou configurações quando fizer sentido
- Quando não tiver certeza de algo, diga claramente que não sabe
- Nunca invente APIs ou parâmetros que não existem
- Se a pergunta estiver fora do seu escopo, diga isso claramente
- Cite as fontes dos documentos quando usar a base de conhecimento
 
Formato das respostas:
- Use markdown para formatar queries e blocos de código
- Seja objetivo — evite introduções longas
- Quando der um exemplo, explique o que cada parte faz

Passo 4 — Testar o agente

No painel de teste à direita da tela:

O que é ELSER e como ele se diferencia de modelos de dense vector como o E5?
Meu cluster está com status yellow. Quais são as causas mais comuns
e como diagnostico?
Como configuro uma ILM policy para logs que devem ficar hot por 7 dias,
warm por 30 dias e serem deletados após 90 dias?

Passo 5 — Salvar e usar o agente

  1. Nomeie como ElasticBot e clique em Save
  2. Abra o chat do Agent Builder e selecione ElasticBot na lista de agentes
  3. Faça uma das perguntas do passo anterior e observe, na resposta, quais tools o agente decidiu usar

O agente agora está disponível para todos os usuários que acessam esse Kibana.


12.7 - Search Playground

O Search Playground é a interface de RAG do Kibana para desenvolvedores. Com o EIS conectado, os LLMs gerenciados pela Elastic estão disponíveis automaticamente — sem API key de provedor externo.

Na 9.4, o Playground está descontinuado (deprecated) em favor do Agent Builder. Ele continua disponível e é útil para entender a mecânica do RAG, mas novos projetos devem usar o Agent Builder.

Diferença do Agent Builder: o Playground expõe toda a mecânica do RAG — a query Elasticsearch executada por baixo, o contexto enviado ao LLM e o código Python gerado. É uma ferramenta de experimentação e desenvolvimento, não de uso final pela equipe.

Acesso: Menu → Playground (ou busca global)

Com o EIS conectado, o Playground mostra "LLM Connected". Selecione um índice como data source, faça perguntas e explore:

  • Query: a query Elasticsearch executada para buscar o contexto
  • View code: o pipeline RAG completo em Python, pronto para usar em uma aplicação

12.8 - Laboratório 4: busca semântica em português com E5

Neste laboratório vamos criar um índice com conteúdo em português, usar o modelo E5 multilingual via EIS e comparar os resultados de busca BM25, semântica e híbrida.

Passo 1 — Criar o índice com semantic_text usando E5

PUT /artigos-tecnologia
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "titulo": {
        "type": "text",
        "copy_to": "titulo_semantico"
      },
      "conteudo": {
        "type": "text",
        "copy_to": "conteudo_semantico"
      },
      "categoria": { "type": "keyword" },
      "titulo_semantico": {
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elastic"
      },
      "conteudo_semantico": {
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elastic"
      }
    }
  }
}

Explicação:

  • titulo e conteudo são campos text — para busca BM25 e recuperação do texto
  • titulo_semantico e conteudo_semantico são campos semantic_text com o E5 multilingual
  • copy_to copia automaticamente o valor de titulo para titulo_semantico e de conteudo para conteudo_semantico — você indexa apenas nos campos originais e os campos semânticos ficam sincronizados
  • O E5 foi escolhido porque os conteúdos estão em português
  • number_of_replicas: 0 porque o lab-es tem um único node

Passo 2 — Indexar documentos em português

POST /_bulk?refresh=true
{ "index": { "_index": "artigos-tecnologia", "_id": "1" } }
{ "titulo": "Falhas de rede em ambientes corporativos", "categoria": "infraestrutura", "conteudo": "Problemas de conectividade podem ocorrer em múltiplas camadas. Na camada física, falhas de cabo, switch ou placa de rede. Na camada de rede, roteamento incorreto, tabelas ARP desatualizadas ou MTU incompatível. Na camada de transporte, timeout de conexão TCP, janelas de congestionamento mal configuradas. Na camada de aplicação, DNS com TTL muito longo ou firewall bloqueando portas específicas. O diagnóstico começa pela camada mais baixa e sobe progressivamente." }
{ "index": { "_index": "artigos-tecnologia", "_id": "2" } }
{ "titulo": "Queries lentas em bancos de dados relacionais", "categoria": "banco-de-dados", "conteudo": "Performance degradada em banco de dados tem causas variadas. A mais comum é ausência de índice nas colunas usadas em cláusulas WHERE e JOIN. O EXPLAIN ANALYZE revela o plano de execução e identifica table scans desnecessários. JOINs em tabelas muito grandes sem filtros seletivos multiplicam o custo. Subqueries correlacionadas executam uma vez para cada linha da query externa. Estatísticas desatualizadas fazem o query planner escolher planos ineficientes — execute ANALYZE para atualizá-las." }
{ "index": { "_index": "artigos-tecnologia", "_id": "3" } }
{ "titulo": "Escalabilidade automática com Kubernetes", "categoria": "cloud", "conteudo": "O Horizontal Pod Autoscaler escala automaticamente o número de pods com base em métricas. A configuração mais simples usa utilização de CPU: quando a média ultrapassa o threshold configurado, novos pods são criados automaticamente. Métricas customizadas permitem escalar com base em requisições por segundo ou tamanho da fila de processamento. O Cluster Autoscaler complementa o HPA escalando os nodes do cluster quando não há recursos disponíveis para alocar novos pods." }
{ "index": { "_index": "artigos-tecnologia", "_id": "4" } }
{ "titulo": "Segurança em APIs REST: autenticação e autorização", "categoria": "segurança", "conteudo": "APIs devem implementar autenticação e autorização como camadas separadas. JWT é o padrão mais usado para autenticação stateless: o servidor gera um token assinado com as informações do usuário e o cliente o envia em cada requisição no header Authorization. OAuth 2.0 é o framework de autorização para delegar acesso a recursos de terceiros sem compartilhar credenciais. Ataques comuns incluem injection de comandos, broken object level authorization e ausência de rate limiting que permite brute force." }
{ "index": { "_index": "artigos-tecnologia", "_id": "5" } }
{ "titulo": "Monitoramento de aplicações com métricas e alertas", "categoria": "observabilidade", "conteudo": "Monitoramento eficaz usa os três pilares: logs para registrar o que aconteceu, métricas para medir o estado atual do sistema, traces para rastrear o caminho percorrido por cada requisição. Métricas de sistema como CPU, memória e disco indicam pressão de recursos. Métricas de aplicação como latência, taxa de erros e throughput indicam a experiência real do usuário. Alertas devem usar thresholds dinâmicos baseados em comportamento histórico para evitar fadiga de alertas com notificações excessivas." }
{ "index": { "_index": "artigos-tecnologia", "_id": "6" } }
{ "titulo": "Cache: estratégias para reduzir o tempo de resposta", "categoria": "performance", "conteudo": "Cache reduz latência eliminando computação repetida ou acesso desnecessário ao banco de dados. Cache em memória como Redis é o mais rápido mas volátil — os dados são perdidos em caso de reinicialização. A invalidação de cache é o problema mais difícil: invalidar cedo demais gera pressão no backend com muitas requisições, invalidar tarde demais serve dados desatualizados aos usuários. Em sistemas distribuídos, cache inconsistente entre múltiplos nodes pode causar comportamento imprevisível e difícil de depurar." }
{ "index": { "_index": "artigos-tecnologia", "_id": "7" } }
{ "titulo": "Docker e containers: boas práticas para produção", "categoria": "cloud", "conteudo": "Containers em produção exigem atenção a aspectos que não aparecem em desenvolvimento. Imagens devem ser mínimas para reduzir a superfície de ataque — use imagens base slim ou distroless. Nunca execute processos como root dentro do container. Secrets e senhas não devem estar na imagem — injete como variáveis de ambiente em runtime ou use cofres de segredos como HashiCorp Vault. Defina sempre resource requests e limits de CPU e memória para evitar que um container monopolize os recursos do node." }
{ "index": { "_index": "artigos-tecnologia", "_id": "8" } }
{ "titulo": "Depuração de aplicações Python em produção", "categoria": "desenvolvimento", "conteudo": "Depurar Python em produção exige ferramentas não invasivas que não interrompam o serviço. Logging estruturado em JSON facilita a análise com campos consistentes como timestamp, nível de severidade, nome do serviço e trace ID. O py-spy permite profiling de CPU sem parar a aplicação, gerando flamegraphs que revelam hotspots. Memory leaks são identificados com tracemalloc ou objgraph — procure objetos que crescem indefinidamente. Ferramentas APM como o Elastic APM capturam automaticamente queries SQL lentas, chamadas HTTP externas e exceções com stack trace completo." }
{ "index": { "_index": "artigos-tecnologia", "_id": "9" } }
{ "titulo": "Sistemas de mensageria: RabbitMQ e Kafka", "categoria": "arquitetura", "conteudo": "RabbitMQ e Kafka atendem necessidades diferentes. RabbitMQ é um message broker com roteamento flexível e entrega garantida — adequado para filas de tarefas, RPC assíncrono e integração entre sistemas heterogêneos. Kafka é uma plataforma de streaming com throughput altíssimo e retenção configurável de eventos — adequado para event sourcing, pipelines de dados em tempo real e processamento de streams. A diferença fundamental: no RabbitMQ as mensagens são consumidas e removidas da fila. No Kafka os eventos são retidos e múltiplos consumidores podem ler de forma independente e no seu próprio ritmo." }
{ "index": { "_index": "artigos-tecnologia", "_id": "10" } }
{ "titulo": "Git: fluxos de trabalho para times distribuídos", "categoria": "desenvolvimento", "conteudo": "Times distribuídos precisam de convenções claras no Git para evitar conflitos e garantir qualidade. Gitflow usa branches de feature, develop e release — adequado para produtos com ciclos de release bem definidos. Trunk-based development mantém uma única branch principal com feature flags para isolar funcionalidades incompletas — adequado para times que praticam integração e entrega contínua. Pull requests devem ter escopo pequeno e focado em uma única mudança para facilitar revisão e reduzir conflitos de merge." }

Os embeddings dos campos semantic_text são gerados pelo EIS durante a indexação, por isso o _bulk demora um pouco mais que o normal. Confira que os documentos foram indexados:

GET /artigos-tecnologia/_count

A contagem deve ser 10.

Passo 3 — Busca por palavras-chave (BM25)

Execute e anote os resultados — quais artigos retornaram e com qual score:

GET /artigos-tecnologia/_search
{
  "query": {
    "match": {
      "conteudo": "sistema lento demora para responder"
    }
  },
  "_source": ["titulo", "categoria"]
}

O BM25 vai retornar artigos que contêm as palavras "lento", "demora" ou "responder". Artigos sobre cache e queries lentas de banco provavelmente não aparecerão mesmo sendo relacionados ao problema.

Passo 4 — Busca semântica com E5

GET /artigos-tecnologia/_search
{
  "query": {
    "semantic": {
      "field": "conteudo_semantico",
      "query": "sistema lento demora para responder"
    }
  },
  "_source": ["titulo", "categoria"]
}

Compare com o resultado anterior. O E5 deve retornar artigos sobre cache (reduz latência), queries lentas de banco de dados e monitoramento de latência — o modelo entendeu que "sistema lento que demora para responder" está semanticamente relacionado a performance, latência e tempo de resposta em português.

Passo 5 — Busca híbrida com RRF

Para campos semantic_text no RRF, o segundo retriever usa match no campo semântico — o Elasticsearch detecta automaticamente o tipo e executa busca semântica (não BM25):

GET /artigos-tecnologia/_search
{
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "match": {
                "conteudo": "sistema lento demora para responder"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "match": {
                "conteudo_semantico": "sistema lento demora para responder"
              }
            }
          }
        }
      ],
      "rank_window_size": 10,
      "rank_constant": 60
    }
  },
  "_source": ["titulo", "categoria"]
}

Compare os três resultados. O RRF produz um ranking diferente de ambas as abordagens individuais — documentos relevantes em ambas as listas sobem no resultado final.

Passo 6 — Busca semântica com filtro

Busca semântica restrita a uma categoria específica:

GET /artigos-tecnologia/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "semantic": {
            "field": "conteudo_semantico",
            "query": "como melhorar a velocidade e desempenho"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "categoria": "performance"
          }
        }
      ]
    }
  },
  "_source": ["titulo", "categoria"]
}

O filtro é executado no filter context — sem cálculo de score, com cache. A busca semântica é executada no must context — com score. Resultado: apenas artigos da categoria "performance" que sejam semanticamente relevantes para a query.

Passo 7 — Busca em título e conteúdo com boost

Priorizar documentos onde o título é semanticamente relevante:

GET /artigos-tecnologia/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "semantic": {
            "field": "conteudo_semantico",
            "query": "proteger sistema contra ataques",
            "boost": 1.0
          }
        },
        {
          "semantic": {
            "field": "titulo_semantico",
            "query": "proteger sistema contra ataques",
            "boost": 2.0
          }
        }
      ]
    }
  },
  "_source": ["titulo", "categoria"]
}

O título recebe boost 2.0 — um documento com título semanticamente relevante é priorizado sobre um documento que só menciona o assunto no conteúdo.


12.9 - Laboratório 5: Playground com base de conhecimento em português

Passo 1 — Criar a base de conhecimento

PUT /base-conhecimento-elasticsearch
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "titulo": {
        "type": "text",
        "copy_to": "conteudo_semantico"
      },
      "conteudo": {
        "type": "text",
        "copy_to": "conteudo_semantico"
      },
      "categoria": { "type": "keyword" },
      "conteudo_semantico": {
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elastic"
      }
    }
  }
}
POST /_bulk?refresh=true
{ "index": { "_index": "base-conhecimento-elasticsearch", "_id": "1" } }
{ "titulo": "Diagnóstico de lentidão no Elasticsearch", "categoria": "performance", "conteudo": "Quando o Elasticsearch está lento, o diagnóstico começa pelo heap usage. Se acima de 85%, o garbage collector está causando pausas de latência imprevisível — verifique com GET /_nodes/stats?metric=jvm. O segundo passo é verificar thread pool rejections com GET /_cat/thread_pool?v — rejeições indicam sobrecarga de indexação ou busca. O terceiro passo é ativar o slow log: configure index.search.slowlog.threshold.query.warn em 2s para capturar queries lentas. Use GET /_nodes/hot_threads para ver o que está consumindo CPU no momento." }
{ "index": { "_index": "base-conhecimento-elasticsearch", "_id": "2" } }
{ "titulo": "Boas práticas de sizing de shards", "categoria": "arquitetura", "conteudo": "O tamanho ideal de um shard é entre 10GB e 50GB, com menos de 200 milhões de documentos. Shards maiores que 50GB ficam lentos para recovery após falha e para operações de merge. Os nodes master precisam de pelo menos 1GB de heap para cada 3000 índices do cluster, e o heap dos nodes de dados depende principalmente do número de campos mapeados. O número de shards primários não pode ser alterado após a criação do índice sem split, shrink ou reindex — planeje com antecedência considerando o volume de dados esperado e o número de nodes disponíveis." }
{ "index": { "_index": "base-conhecimento-elasticsearch", "_id": "3" } }
{ "titulo": "Configuração de ILM para ambientes de produção", "categoria": "operações", "conteudo": "Em produção, a ILM policy para logs deve ter rollover baseado em tamanho de shard primário com max_primary_shard_size: 50gb — não em número de documentos, pois o volume varia. Na fase warm, aplique forcemerge com max_num_segments: 1, readonly para bloquear escritas, e reduza réplicas para 1. Na fase cold, reduza réplicas para 0 e use snapshots como proteção. Sempre configure wait_for_snapshot na fase delete em ambientes com requisitos de compliance. Monitore erros com GET /*/_ilm/explain?only_errors=true periodicamente." }
{ "index": { "_index": "base-conhecimento-elasticsearch", "_id": "4" } }
{ "titulo": "Mapping: text vs keyword e quando usar cada um", "categoria": "mapping", "conteudo": "O tipo text é para campos onde você quer busca de texto livre — o Elasticsearch analisa o conteúdo, tokeniza e indexa cada palavra separadamente. Use em descrições, mensagens e conteúdo de logs. O tipo keyword é para valores que você vai filtrar exatamente, ordenar ou agregar — o valor é indexado como está, sem análise. Use em campos de status, UF, email, ID e categoria. O erro mais comum é definir CEP como text — o analyzer divide 01310-100 em tokens 01310 e 100, e filtros por CEP exato nunca encontram nada." }
{ "index": { "_index": "base-conhecimento-elasticsearch", "_id": "5" } }
{ "titulo": "Segurança no Elasticsearch: checklist de produção", "categoria": "segurança", "conteudo": "No Elasticsearch 9.x, segurança vem ativada por padrão. O checklist de produção inclui: nunca usar o usuário elastic em aplicações — crie usuários específicos com permissões mínimas necessárias. Configurar TLS no transport layer entre nodes com certificados próprios em produção. Usar o Keystore para armazenar senhas em configurações do Logstash e Beats — nunca em texto plano. Restringir network.host — não use 0.0.0.0 em produção sem firewall configurado. Habilitar audit logging para rastrear acessos e operações em dados sensíveis." }

O documento 2 foi atualizado em relação à versão original do curso: a regra de "20 shards por GB de heap" foi substituída pela orientação atual da Elastic, baseada em índices e campos mapeados.

Passo 2 — Usar no Playground

  1. Abra o Playground
  2. Clique em Add data sources e selecione base-conhecimento-elasticsearch
  3. Faça perguntas em português:
Como diagnostico lentidão no meu cluster Elasticsearch?
Qual é a melhor configuração de ILM para logs em produção?
Quando devo usar text e quando devo usar keyword no mapping?
  1. Clique em Query para ver a query Elasticsearch executada por baixo
  2. Clique em View code para ver o pipeline RAG completo em Python

12.10 - Laboratório 6: LLM local no cluster com Ollama

Este laboratório monta a alternativa sem Elastic Cloud: um LLM rodando no próprio cluster Kubernetes, com o Ollama, conectado ao Kibana pelo conector OpenAI. É o caminho para ambientes sem acesso à Elastic Cloud ou que não podem enviar dados para fora.

Recursos: o Ollama pede de 3Gi a 5Gi de memória. Rode este laboratório com os coletores e outros componentes removidos e use um modelo pequeno. Um modelo de 1B parâmetros responde, mas com qualidade bem inferior à dos LLMs gerenciados — o objetivo aqui é o fluxo, não a qualidade. A VM precisa de acesso à internet para baixar a imagem e o modelo.

Passo 1 — Criar o manifesto do Ollama

cat <<'EOF' > manifests/ollama.yaml
# LLM local (Ollama) rodando NO cluster — expõe API compatível com OpenAI.
# Endpoint p/ o conector do Kibana: http://ollama.elastic.svc:11434/v1
# ATENÇÃO (16 GB): pesado. Use modelo pequeno (llama3.2:1b). Ideal 32 GB. Rode isolado.
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ollama-models
  namespace: elastic
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 15Gi
  storageClassName: local-path
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ollama
  namespace: elastic
  labels: { app: ollama }
spec:
  replicas: 1
  selector:
    matchLabels: { app: ollama }
  template:
    metadata:
      labels: { app: ollama }
    spec:
      containers:
        - name: ollama
          image: ollama/ollama:latest
          ports:
            - containerPort: 11434
          env:
            - name: OLLAMA_HOST
              value: "0.0.0.0"
          resources:
            requests:
              memory: 3Gi
              cpu: "2"
            limits:
              memory: 5Gi
          volumeMounts:
            - name: models
              mountPath: /root/.ollama
      volumes:
        - name: models
          persistentVolumeClaim:
            claimName: ollama-models
---
apiVersion: v1
kind: Service
metadata:
  name: ollama
  namespace: elastic
spec:
  selector: { app: ollama }
  ports:
    - port: 11434
      targetPort: 11434
  type: ClusterIP
EOF
  • PersistentVolumeClaim ollama-models: guarda os modelos baixados, para não baixá-los de novo se o pod for recriado;
  • Deployment ollama: o servidor do Ollama, que expõe uma API compatível com a da OpenAI na porta 11434;
  • Service ollama: dá ao Kibana o endereço http://ollama.elastic.svc:11434, dentro do cluster.

Este não é um recurso do ECK: o Ollama é um Deployment comum do Kubernetes, que convive no mesmo namespace com o Elasticsearch e o Kibana.

A imagem ollama/ollama:latest muda com o tempo. Em produção, fixe uma versão da imagem.

Passo 2 — Aplicar e baixar um modelo pequeno

kubectl apply -f manifests/ollama.yaml
kubectl -n elastic rollout status deploy/ollama
kubectl -n elastic exec deploy/ollama -- ollama pull llama3.2:1b

Resultado esperado: deployment "ollama" successfully rolled out e, depois do download, a mensagem success do ollama pull.

Passo 3 — Testar a API compatível com a OpenAI

pkill -f "port-forward.*11434" || true
nohup kubectl -n elastic port-forward service/ollama 11434:11434 > /tmp/pf-ollama.log 2>&1 &
sleep 2
curl -s http://localhost:11434/v1/models

Resultado esperado: um JSON com o modelo llama3.2:1b na lista data.

Passo 4 — Criar o conector no Kibana

  1. Abra Stack Management → Connectors e clique em Create connector.
  2. Escolha o tipo OpenAI.
  3. Preencha:
    • Connector name: llm-local
    • Select an OpenAI provider: Other (OpenAI Compatible Service)
    • URL: http://ollama.elastic.svc:11434/v1/chat/completions
    • Default model: llama3.2:1b
    • API key: qualquer valor não vazio, como ollama (o Ollama não valida a chave, mas o campo é obrigatório)
  4. Clique em Save & test e envie uma mensagem de teste.

A URL usa o nome do Service dentro do cluster: o Kibana, que roda no mesmo cluster, alcança o Ollama sem túnel.

Passo 5 — Usar o LLM local

  1. No Agent Builder, crie um agente com o conector llm-local como modelo e faça uma pergunta sobre um dos índices dos laboratórios anteriores.
  2. No Playground, selecione o conector llm-local e o índice base-conhecimento-elasticsearch, e repita uma das perguntas do laboratório anterior.

Compare as respostas com as dos LLMs gerenciados: o fluxo de RAG é o mesmo, muda apenas o LLM que redige a resposta.

Para o Agent Builder usar tools com um LLM local, o modelo precisa suportar tool calling (chamada de funções). Modelos muito pequenos podem ignorar as tools ou responder de forma inconsistente; nesse caso, teste com um modelo maior, se a memória da VM permitir.

Passo 6 (opcional) — Embeddings no node de ML do próprio cluster

Para uma busca semântica totalmente local, use o endpoint .multilingual-e5-small-elasticsearch, que roda o E5 no node de ML do lab-es:

PUT /base-conhecimento-local
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "titulo":   { "type": "text", "copy_to": "conteudo_semantico" },
      "conteudo": { "type": "text", "copy_to": "conteudo_semantico" },
      "conteudo_semantico": {
        "type": "semantic_text",
        "inference_id": ".multilingual-e5-small-elasticsearch"
      }
    }
  }
}
POST /_reindex?wait_for_completion=true
{
  "source": { "index": "base-conhecimento-elasticsearch", "_source": ["titulo", "conteudo"] },
  "dest":   { "index": "base-conhecimento-local" }
}

Na primeira indexação, o Elasticsearch baixa e implanta o modelo E5 no node de ML, o que pode levar alguns minutos (e a primeira requisição pode expirar e precisar ser repetida). Confira a implantação e faça uma busca:

GET /_ml/trained_models/_stats?filter_path=trained_model_stats.model_id,trained_model_stats.deployment_stats.state
GET /base-conhecimento-local/_search
{
  "query": {
    "semantic": {
      "field": "conteudo_semantico",
      "query": "meu cluster está lento"
    }
  },
  "_source": ["titulo"]
}

⚠️ VALIDAR: conferir no laboratório se o modelo E5 cabe na memória de ML disponível no lab-es (limite de 4Gi) junto com o restante da carga.


12.11 - Limpeza do ambiente

Remova o Ollama e feche o túnel:

pkill -f "port-forward.*11434" || true
kubectl delete -f manifests/ollama.yaml

Resultado esperado: o Deployment, o Service e o PVC ollama-models são removidos, liberando memória e disco.

No Console, remova os índices dos laboratórios:

DELETE /artigos-tecnologia,base-conhecimento-elasticsearch,base-conhecimento-local

Se implantou o E5 local, não é preciso pará-lo: o endpoint padrão usa alocações adaptativas com mínimo de zero, e a implantação é reduzida automaticamente quando o modelo fica sem uso, liberando a memória de ML. Confira o estado com:

GET /_ml/trained_models/_stats?filter_path=trained_model_stats.model_id,trained_model_stats.deployment_stats.allocation_status

Por fim, para não consumir créditos da Elastic Cloud, desconecte o Elastic Inference Service e o Cloud Connect na página Cloud Connect do Kibana, e apague o conector llm-local em Stack Management → Connectors.


Resumo do capítulo

  • A busca no Elasticsearch evoluiu do BM25 para a busca semântica (ELSER em inglês, E5 multilingual em português), a híbrida com RRF, o RAG e os agentes.
  • O campo semantic_text gera os embeddings na indexação e escolhe o tipo de busca automaticamente; copy_to mantém o campo text e o semântico sincronizados.
  • O Cloud Connect leva o Elastic Inference Service (embeddings, rerankers e LLMs gerenciados) também para clusters ECK, com licença Enterprise ou trial e uma conta Elastic Cloud.
  • Sem nuvem, os embeddings rodam em um node de ML do cluster (.elser-2-elasticsearch, .multilingual-e5-small-elasticsearch) e o LLM pode ser um serviço compatível com a API da OpenAI, como o Ollama rodando no próprio Kubernetes, ligado pelo conector OpenAI com o provedor Other (OpenAI Compatible Service).
  • O AI Assistant conversa sobre o cluster e a documentação; o Agent Builder (GA desde a 9.3) cria agentes com instruções, tools e LLM; o Playground expõe a mecânica do RAG e está descontinuado na 9.4.

Origem do conteúdo

12.1 e 12.2 - Fundamentos de busca com IA

12.4 a 12.9 - Laboratórios com EIS

12.10 - LLM local

⚠️ **GitHub.com Fallback** ⚠️