O Elasticsearch conta com integrações nativas com as principais ferramentas e provedores de IA generativa do setor. Confira nossos webinars sobre como ir além do básico do RAG ou criar aplicativos prontos para produção com o banco de dados vetorial da Elastic.
Para criar as melhores soluções de busca para seu caso de uso, inicie um teste gratuito na nuvem ou experimente o Elastic em sua máquina local agora mesmo.
Essa ideia me ocorreu durante uma acirrada e decisiva liga de basquete de fantasia. Eu me perguntei: será que eu conseguiria criar um agente de IA que me ajudasse a dominar meus confrontos semanais? Com certeza!
Neste artigo, exploraremos como construir um assistente RAG agente usando o Mastra 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 repositório do GitHub para acompanhar; o arquivo README fornece instruções sobre como clonar e executar o aplicativo por conta própria.
Eis como deverá ficar quando tudo estiver montado:

Nota: Este post do blog complementa o artigo “Criando agentes de IA com o SDK de IA e o Elastic”. Se você é iniciante no estudo de agentes de IA em geral e em suas possíveis aplicações, comece por aí.
Visão geral da arquitetura
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.
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.
Na interface, usamos o Vite para criar rapidamente um aplicativo web React que fornece uma interface de chat para enviar perguntas ao agente e receber suas respostas.
Por fim, temos o Elasticsearch, que armazena estatísticas de jogadores e dados de confrontos que o agente pode consultar e agregar.

Histórico
Vamos revisar alguns conceitos fundamentais:
O que é RAG agentivo?
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 aqui.
Ao escolher uma estrutura, por que ir além do AI-SDK?
Existem muitas estruturas de agentes de IA disponíveis e você provavelmente já ouviu falar das mais populares, como CrewAI, AutoGen e LangGraph. A maioria dessas estruturas compartilha um conjunto comum de funcionalidades, incluindo suporte para diferentes modelos, uso de ferramentas e gerenciamento de memória.
Segue abaixo uma tabela comparativa de frameworks elaborada por Harrison Chase (CEO da LangChain).
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.
Considerações sobre estruturas e escolha de modelos
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.
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.
Basquete de fantasia da NBA
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.
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.
É 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.
Agora que você já conhece alguns conceitos básicos sobre RAG agentivo e basquete fantasy da NBA, vamos ver como funciona na prática.
Construindo o projeto
Se você ficar preso em algum ponto ou não quiser construir tudo do zero, consulte o repositório.
O que abordaremos
- Estruturando o projeto:
- Backend (Mastra): Use o comando `npx create mastra@latest` para criar a estrutura do backend e definir a lógica do agente.
- Frontend (Vite + React): Use o comando `npm create vite@latest` para criar a interface de chat do frontend para interação com o agente.
- Configurando variáveis de ambiente
- Instale o dotenv para gerenciar variáveis de ambiente.
- Crie um arquivo .env arquive e forneça as variáveis necessárias.
- Configurando o Elasticsearch
- Crie um cluster Elasticsearch (localmente ou na nuvem).
- Instale o cliente oficial do Elasticsearch.
- Garanta que as variáveis de ambiente estejam acessíveis.
- Estabelecer conexão com o cliente.
- Ingestão em massa de dados da NBA no Elasticsearch
- Crie um índice com os mapeamentos apropriados para habilitar agregações.
- Importar em massa estatísticas de jogo de jogadores de um arquivo CSV para um índice do Elasticsearch.
- Definir agregações do Elasticsearch
- Consulta para calcular as médias históricas contra um adversário específico.
- Consulta para calcular as médias da temporada contra um adversário específico.
- Arquivo utilitário de comparação de jogadores
- Consolida funções auxiliares e agregações do Elasticsearch.
- Construindo o agente
- Adicione a definição do agente e o prompt do sistema.
- Instale o Zod e defina as ferramentas.
- Adicionar configuração de middleware para lidar com CORS.
- Integrando o frontend
- Utilizando o useChat do AI-SDK para interagir com o agente.
- Crie a interface do usuário para manter conversas formatadas adequadamente.
- Executando o aplicativo
- Inicie tanto o backend (servidor Mastra) quanto o frontend (aplicativo React).
- Exemplos de consultas e uso.
- O que vem a seguir: tornar o agente mais inteligente.
- Adicionando recursos de busca semântica para possibilitar recomendações mais relevantes.
- Habilite consultas dinâmicas movendo a lógica de busca para o servidor Elasticsearch MCP (Model Context Protocol).
Pré-requisitos
- Node.js e npm: 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+).
- Cluster Elasticsearch: Um cluster Elasticsearch ativo, seja localmente ou na nuvem.
- Chave da API da OpenAI: Gere uma na página de chaves da API no portal de desenvolvedores da OpenAI.
Estrutura do projeto

Etapa 1: Estruturando o projeto
- Primeiro, crie o diretório nba-ai-assistant-js e navegue até ele usando:
Backend:
- Utilize a ferramenta de criação do Mastra com o comando:
2. Você deverá receber algumas mensagens no seu terminal. Para a primeira, vamos nomear o backend do projeto:

3. Em seguida, manteremos a estrutura padrão para armazenar os arquivos Mastra, então insira src/.

4. Em seguida, escolheremos a OpenAI como nosso provedor padrão de LLM.

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 .env .

Front-end:
- Volte ao diretório raiz e execute a ferramenta de criação do Vite usando este comando:
npm create vite@latest frontend -- --template react
Isso deverá criar um aplicativo React leve chamado frontend com um modelo específico para React.
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 frontend com seu aplicativo React.
Etapa 2: Configurando as variáveis de ambiente
- Para gerenciar chaves sensíveis, usaremos o pacote
dotenvpara carregar nossas variáveis de ambiente do arquivo .env. arquivo. Navegue até o diretório backend e instaledotenv:
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:
Nota: Certifique-se de que este arquivo seja excluído do seu controle de versão adicionando .env a .gitignore.
Etapa 3: Configurando o Elasticsearch
Primeiro, você precisa de um cluster Elasticsearch ativo. Existem duas opções:
- Opção A: Usar o Elasticsearch Cloud
- Inscreva-se no Elastic Cloud.
- Criar uma nova implantação
- Obtenha o URL do seu endpoint e a chave da API (codificada).
- Opção B: Executar o Elasticsearch localmente
- Instale e execute o Elasticsearch localmente.
- Use http://localhost:9200 como seu endpoint.
- Gere uma chave de API
Instalando o cliente Elasticsearch no servidor:
- Primeiro, instale o cliente oficial do Elasticsearch no diretório do seu backend:
2. Em seguida, crie um diretório chamado lib para armazenar funções reutilizáveis e navegue até ele:
3. Dentro da pasta, crie um novo arquivo chamado elasticClient.js. Este arquivo inicializará o cliente Elasticsearch e o disponibilizará para uso em todo o seu projeto.
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:
5. Agora, inicialize o cliente Elasticsearch usando suas variáveis de ambiente e verifique a conexão:
Agora, podemos importar essa instância de cliente para qualquer arquivo que precise interagir com o seu cluster Elasticsearch.
Etapa 4: Ingestão em massa de dados da NBA no Elasticsearch
Conjunto de dados:
Para este projeto, utilizaremos como referência os conjuntos de dados disponíveis no diretório backend/data do repositório. Nosso assistente da NBA usará esses dados como base de conhecimento para realizar comparações estatísticas e gerar recomendações.
- sample_player_game_stats.csv - 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.)
- playerAndTeamInfo.js - 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.
Implementação:
- No diretório
backend/lib, crie um arquivo chamado playerDataIngestion.js. - Configure as importações, resolva o caminho do arquivo CSV e configure a análise sintática. Novamente, como estamos usando ESM, precisamos reconstruir
__dirnamepara resolver o caminho para o CSV de amostra. Além disso, importaremos o Node.js. módulos integrados,fsereadline, para analisar o arquivo CSV fornecido linha por linha.
Isso prepara você para ler e analisar o CSV de forma eficiente quando chegarmos à etapa de ingestão em massa.
3. Crie um índice com o mapeamento apropriado. Embora o Elasticsearch possa inferir automaticamente os tipos de campo com mapeamento dinâmico, 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 float para estatísticas como pontos, rebotes, etc., para garantir que incluamos valores decimais. Finalmente, queremos adicionar a propriedade de mapeamento dynamic: 'strict' para que o Elasticsearch não mapeie dinamicamente campos não reconhecidos.
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.
5. Então, podemos usar a API Bulk do Elasticsearch com elasticClient.bulk() 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.
6. Execute a função main() abaixo para executar sequencialmente as funções createIndex() e bulkIngestCsv() .
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.
Etapa 5: Definindo e consolidando as agregações do Elasticsearch
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.
1. Navegue até o diretório backend/lib e crie um arquivo chamado elasticAggs.js.
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 filtro bool com 2 condições: uma que corresponde player_id e outra que corresponde a opponent_team_id, para recuperar apenas os jogos relevantes. Não precisamos retornar nenhum documento, só nos interessam as agregações, então definimos size:0. No bloco aggs , executamos várias agregações de métricas em paralelo em campos como points, rebounds, assists, steals, blocks e fg_percentage 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.
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 bool tem uma condição adicional para game_date. O campo game_date tem que estar dentro do intervalo da temporada atual da NBA. Neste caso, o intervalo está entre 2024-10-01 e 2025-06-30. Essa condição adicional abaixo garante que as agregações subsequentes isolarão apenas os jogos desta temporada.
Etapa 6: Ferramenta de comparação de jogadores
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:
1. Crie um novo arquivo comparePlayers.js no diretório backend/lib .
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.
Etapa 7: Construindo o agente
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.
Definindo o agente
1. Navegue até o arquivo index.ts dentro do diretório backend/src/mastra/agents e adicione a definição do agente. Você pode especificar campos como:
- Nome: Dê ao seu agente um nome que será usado como referência quando ele for chamado na interface.
- Instruções/mensagem do sistema: 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.
- Modelo: Qual modelo de aprendizagem de linguagem (LLM) usar (o Mastra suporta modelos OpenAI, antrópicos, locais, etc.).
- Ferramentas: Uma lista de funções de ferramentas que o agente pode chamar.
- Memória: (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.
Ferramentas de definição
- Navegue até o arquivo index.ts dentro do diretório
backend/src/mastra/tools. - Instale o Zod usando o comando:
3. Adicionar definições de ferramentas. Observe que importamos a função dentro do arquivo comparePlayers.js como a função principal que o agente usará ao chamar esta ferramenta. Usando a função createTool() do Mastra, vamos registrar nosso playerComparisonTool. Os campos incluem:
idEsta é uma descrição em linguagem natural para ajudar o agente a entender o que a ferramenta faz.input schemaPara definir o formato da entrada para a ferramenta, o Mastra utiliza o esquema Zod , 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.descriptionEsta é uma descrição em linguagem natural para ajudar o agente a entender quando ligar e usar a ferramenta.executeA lógica que é executada quando a ferramenta é chamada. No nosso caso, estamos usando uma função auxiliar importada para retornar estatísticas de desempenho.
Adicionando middleware para lidar com CORS
Adicione um middleware no servidor Mastra para lidar com CORS. 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 documentação do Mastra para que nosso backend permita essas solicitações do frontend.
1. Navegue até o arquivo index.ts dentro do diretório backend/src/mastra e adicione a configuração para CORS:
origin: ['http://localhost:5173']- Permite solicitações somente deste endereço (endereço padrão do Vite)
allowMethods: ["GET", "POST"]- Métodos HTTP permitidos. Na maioria das vezes, será utilizado o método POST.
allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],- Essas configurações definem quais cabeçalhos personalizados podem ser usados nas solicitações.
Etapa 8: Integrando o frontend
Este componente React fornece uma interface de chat simples que se conecta ao agente de IA Mastra usando o gancho useChat() de @ai-sdk/react. 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 react-markdown para formatar a resposta corretamente.
1. No diretório frontend, instale o pacote @ai-sdk/react para usar o gancho useChat().
2. Ainda no mesmo diretório, instale o React Markdown para que possamos formatar corretamente a resposta gerada pelo agente.
3. Implemente useChat(). 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:
api: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.onToolCallEste comando é executado sempre que o agente chama uma ferramenta; estamos usando-o para rastrear quais ferramentas nosso agente está chamando.onFinishEsta ação é executada depois que o agente conclui uma resposta completa. Mesmo que tenhamos habilitado o streaming,onFinishainda 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.
4. Finalmente, acesse o componente ChatUI.jsx no diretório frontend/components para criar a interface do usuário para manter nossa conversa. Em seguida, envolva a resposta em um componente ReactMarkdown para formatar corretamente a resposta do agente.
Etapa 9: Executando o aplicativo
Parabéns! Agora você está pronto para executar o aplicativo. Siga estes passos para iniciar tanto o backend quanto o frontend.
- Em uma janela de terminal, partindo do diretório raiz, navegue até o diretório de backend e inicie o servidor Mastra:
2. Em outra janela do terminal, partindo do diretório raiz, navegue até o diretório frontend e inicie o aplicativo React:
3. Acesse seu navegador e navegue até:
Você deverá conseguir visualizar a interface de bate-papo. Experimente estas sugestões:
- "Compare LeBron James e Stephen Curry"
- "Quem devo escolher entre Jayson Tatum e Luka Doncic?"
O que vem a seguir: tornar o agente mais inteligente.
Para tornar o assistente mais proativo e as recomendações mais relevantes, adicionarei algumas melhorias importantes na próxima versão.
Busca semântica para notícias da NBA
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.
Pesquisa dinâmica com o servidor Elasticsearch MCP
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 aqui.
Essas mudanças já estão em andamento, fique ligado!
Conclusão
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:
- Fundamentos do RAG agético 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.
- Elasticsearch 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).
- O framework Mastra e como ele simplifica a criação desses agentes para desenvolvedores no ecossistema JavaScript.
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 GitHub. Sinta-se à vontade para cloná-lo e fazer alterações. Agora, vá ganhar essa liga de fantasia!




