Alura > Cursos de Inteligência Artificial > Cursos de IA para Programação > Conteúdos de IA para Programação > Primeiras aulas do curso GPT Code Interpreter e Python: crie assistentes e aumente sua produtividade

GPT Code Interpreter e Python: crie assistentes e aumente sua produtividade

Desenhando testes e casos de uso com a IA Generativa - Apresentação

Olá, pessoal! Sou André Santana, um dos instrutores da Escola de Inteligência Artificial da Alura.

André Santana se identifica como um homem branco. Tem olhos castanhos, cabelos curtos, barba e bigode médios também na cor castanha. Usa um óculos redondo e uma camiseta cinza. Ao fundo, estúdio com iluminação arroxeada. À direita, uma estante com decorações e luzes amareladas em tom quente.

O que vamos aprender?

Neste curso de Interpretação de Código com OpenAI, vamos aprender a criar assistentes de uma forma modular, para que você possa escalar o seu projeto criando novos assistentes sem nenhum problema.

Vamos abordar quatro assistentes específicos para fazer:

  1. Gestão de commits, sugerindo títulos e texto descritivo de um script que você queira commitar;
  2. Documentação de um script;
  3. Revisão e sugestão boas práticas através do uso de IA generativa;
  4. Criação de testes unitários.

Além disso, vamos entender como trabalhar com versionamento de arquivos, através da geração de hashes para otimizar a gestão de commits.

Público-alvo

Este conteúdo é para quem gosta de Python e já tem alguma experiência, pois vamos trabalhar bastante com orientação a objetos durante o desenvolvimento desse projeto.

É também para quem quer otimizar e aprimorar o seu processo de desenvolvimento, aumentando a produtividade na hora de escrever um código ou de trabalhar em equipe na gestão de um projeto.

Pré-requisitos

Recomendamos que você acesse os cursos anteriores da formação de OpenIA e Python, para que possa aproveitar melhor este curso.

Todo esse processo envolvendo o code interpreter vai tratar do uso de um projeto de análise de dados na Twitch. Mas nosso foco maior será criar assistentes para poder fazer a gestão e a manutenção de um projeto, seja ele qual for.

Além dos vídeos, lembre-se também que temos nossas atividades, comunidade do Discord da Alura e também o apoio do nosso fórum. Até a primeira aula!

Desenhando testes e casos de uso com a IA Generativa - Apresentando o projeto base

Vamos começar a entender o projeto que vamos utilizar nesse curso de OpenAI, que envolve o Code Interpreter. Este é um curso com um foco maior na parte de otimização de código e, para isso, vamos passar por algumas etapas.

Conhecendo o projeto

O projeto base que vamos utilizar tem a capacidade de analisar um conjunto de dados fornecidos pela Twitch. Nesse conjunto de dados, criamos uma aplicação que lê um arquivo CSV.

Se acessamos o diretório de "Projeto_Dados > base_dados", o CSV estará disponível com os dados da Twitch sobre um conjunto de canais de streaming com a quantidade de minutos assistidos e transmitidos, o número de visualizações e mais.

Queremos analisar essa aplicação utilizando a OpenAI. A partir disso, a inteligência artificial vai sugerir algumas mudanças que podem ser feitas e afetar o processo de desenvolvimento da pessoa desenvolvedora.

Para poder executar e entender essa aplicação, vamos acessar o script chamado main.py.

main.py:

from twitch_analytics import DataLoader, dataanalytics, DataVisualizer, ConsoleInterface

def main():
    filepath = './Projeto_Dados/base_dados/twitchdata-update.csv'
    data_loader = DataLoader(filepath)
    analytics = dataanalytics(data_loader.get_data())
    visualizer = DataVisualizer(analytics)
    console_interface = ConsoleInterface(data_loader, analytics, visualizer)
    console_interface.run()

if __name__ == "__main__":
    main()

Nesse script, definimos o caminho dos nossos dados em filepath, que passamos para uma classe chamada DataLoader.

Carregando dados

O que esse DataLoader faz? Ele vai receber esse caminho e vai garantir que os dados que estejam associados dentro do CSV sejam convertidos em um DataFrame, utilizando a biblioteca Pandas (responsável por trabalhar com dados em Python). Desse modo, caso queiramos resgatar esses dados, possamos fazer isso através do método get_data().

data_loader.py:

import pandas as pd

class DataLoader:
    def __init__(self, filepath):
        self.data = pd.read_csv(filepath)

    def get_data(self):
        return self.data

Analisando os dados

Voltando para o main(), também temos uma segunda classe chamada dataanalytics. Ela tem o nome diferente de forma proposital, porque vamos utilizar a OpenAI para poder sugerir boas práticas de desenvolvimento ao longo do curso.

Por conta disso, vamos extrair os dados do DataLoader e instanciar esse objeto de dataanalytics.

data_analytics.py:

import pandas as pd
from pandas import DataFrame
import json

class dataanalytics:

    def __init__(self, data : DataFrame):
        self.data = data
        self.copy_data = data.copy()
        self.data_backup = self.data
        self.temp_data = pd.DataFrame()
        self.cache = {}

    def getTopChannelByViewers(self, top_n=10):
    # código omitido…

    def getStreamerStats(self, streamer : str):
    # código omitido…

    def getTopChannelByStreamTime(self, top_n=10):
    # código omitido…

    def getAverageViewersByChannel(self, top_n=10):
    # código omitido…

Confira o arquivo data_analytics.py completo

Dentro dessa classe, existe um construtor que recebe o DataFrame, inicializa os dados para poder utilizá-los posteriormente e cria um conjunto de variáveis para poder garantir que os dados possam ser manipulados.

Temos alguns códigos redundantes, mas isso será importante para poder verificar como a OpenAI, mais para frente, consegue nos ajudar a resolver problemas de redundância e refatoração de código.

Além disso, temos algumas funções que vão nos ajudar a interagir com os dados. Com a função getTopChannelByViewers(), conseguimos verificar quais são os canais que têm mais visualizações. Já a função getStreamerStats() informa os status de um streamer em específico, retornando informações como a média de visualizações, o tempo total que esse canal foi assistido e também o tempo de streaming.

Logo abaixo, a função getTopChannelByStreamTime() traz o top tier de canais de streaming, enquanto getAverageViewersByChannel() retorna a média de visualizações por canal.

São algumas funções, que naturalmente estariam em um projeto de ciência de dados, mas vamos utilizá-las como base para poder sugerir mudanças e afetar a pipeline de desenvolvimento, aproveitando os recursos da OpenAI não só para poder criar, mas para dar sugestões de otimização.

Visualizando os dados

Por fim, se voltarmos para o script do main.py, temos uma última instância de classe na linha 7 que é o DataVisualizer. Ele recebe um analytics como parâmetro para poder extrair esses dados e criar as visualizações que queremos. Instanciamos esse objeto na variável visualizer também na linha 7.

data_visualizer.py:

import matplotlib.pyplot as plt
from .data_analytics import dataanalytics

class DataVisualizer:
    def __init__(self, analytics : dataanalytics):
        self.analytics = analytics

    def plot_top_channel_by_viewers(self, top_n=10):
    # código omitido…

    def plot_top_channel_by_stream_time(self, top_n=10):
    # código omitido…

    def plot_average_viewers_by_channel(self, top_n=10):
    # código omitido…

Confira o arquivo data_visualizer.py completo

O construtor da classe DataVisualizer recebe o analytics como parâmetro. Nessa classe, também existem alguns métodos que nos permitem plotar alguns gráficos. Fazemos isso utilizando o Matplotlib, importado na linha 1, para poder desenhar gráficos de barras.

O método plot_top_channel_by_viewers() faz o plot das visualizações por canal, enquanto o plot_top_channel_by_stream_time() plota um gráfico de barras por tempo de streaming feito.

Por fim, em plot_average_viewers_by_channel(), temos uma última representação gráfica que envolve a parte de visualizações médias por canal. Caso um canal tenha mais de uma ocorrência no DataFrame, vamos identificar essa recorrência e calcular as visualizações médias.

Conclusão

Vamos utilizar esse projeto como base para poder empregar o uso da IA generativa em 4 momentos distintos.

Primeiro, vamos sugerir textos de títulos e textos de commit de descrição. Em seguida, vamos fazer uma documentação das nossas classes. Em terceiro, propor uma revisão com melhorias e, por último, criar testes unitários.

Até o próximo vídeo!

Desenhando testes e casos de uso com a IA Generativa - Criando a classe do assistente base com Code Interpreter: assistente base

Agora que já conhecemos o nosso projeto e sabemos que trabalharemos com análise de dados, vamos começar a resolver nosso problema. Este consiste em utilizar a OpenAI através do uso de agentes para garantir uma comunicação entre o código desenvolvido e um assistente na OpenAI. Em específico, vamos trabalhar com assistentes que podem analisar código.

Criando assistente base com code interpreter

O primeiro passo será criar um novo script para ter o assistente base. Na raiz do projeto, vamos clicar em "Novo Arquivo" no painel Explorer à esquerda para criar um script chamado assistente_base.py.

Esse assistente base vai garantir que tenhamos uma estrutura base para criar qualquer um dos quatro assistentes que criaremos ao longo deste curso.

Primeiramente, devemos importar as bibliotecas necessárias. Nesse caso, importaremos a biblioteca da OpenAI e também duas bibliotecas que estão dentro do módulo de recursos beta da OpenAI, que são o módulo de assistentes e o módulo de threads.

Para isso, basta acessar a openai e, dentro dela, acessar os tipos de dados através de types. Em seguida, acessar a parte de recursos beta.assistant para importar o Assistant que iremos utilizar.

As threads têm um caminho muito similar a esse. Por isso, podemos duplicar essa linha e mudar a palavra assistant para a palavra thread. E, ao invés de importar a classe Assistant, vamos importar a classe Thread também.

assistente_base.py:

from openai import OpenAI
from openai.types.beta.assistant import Assistant
from openai.types.beta.thread import Thread

Depois, vamos importar uma biblioteca de variáveis de ambiente, dotenv, que é onde vamos extrair a chave da OpenAI, que estará no .env, para ficar oculta na hora de compartilhar o projeto em algum repositório. Vamos utilizar o load_dotenv para isso.

Por último, para poder usar o load_dotenv, vamos importar também o sistema operacional por meio de import os.

from dotenv import load_dotenv
import os

Para dar sequência, vamos usar programação orientada a objetos, para garantir que tenhamos uma solução modular e facilitar a criação de qualquer tipo de assistente posteriormente.

Para isso, vamos criar uma classe chamada AssistenteBase. E, como toda classe que teremos em Python, precisamos trazer para ela um construtor.

A definição desse construtor será sempre marcada com a diretriz def __init__(). Entre parênteses, ele terá um self, pelo menos para poder dizer que se trata de um método de instância, que ele precisa de um objeto para poder chamá-lo.

Feito isso, vamos trazer alguns parâmetros. Sempre que formos criar um assistente novo, vamos entregar a ele um caminho de um arquivo para análise, um nome para esse assistente e um conjunto de instruções, que é o que vai guiar a OpenAI para que ela possa, de fato, interpretar esse arquivo e seguir as instruções dadas.

O primeiro parâmetro será o nome, segundo será um conjunto de instrucoes e o terceiro será um caminho_arquivo. Todas as três variáveis devem estar com o seu tipo explícito e todas elas são do tipo string.

class AssistenteBase():
    def __init__(self, nome: str, instrucoes: str, caminho_arquivo: str):

Com isso, quando formos criar esse objeto, precisamos informar esses três parâmetros para o construtor da classe.

Em sequência, vamos instanciar o cliente da OpenAI. Para isso, vamos criar uma variável de instância, que chamamos de self.cliente. Ela será importante para poder fazer qualquer tipo de comunicação com a API. Vamos indicar que ela será do tipo OpenAI.

Ela recebe o valor baseado no construtor da OpenAI(), passando um único parâmetro, que é a chave de API, ou seja, api_key. E onde vamos extrair essa chave? No sistema operacional, vamos chamar o método os.getenv() para buscar uma chave que estará registrada.

Então, você precisa criar um .env, como já fizemos em outros cursos, da parte de IA generativa com OpenAI, e garantir que a chave esteja salva exatamente com o nome que colocaremos entre as aspas. No nosso caso, colocamos como OPENAI_API_KEY.

class AssistenteBase():
  def __init__(self, nome : str, instrucoes : str, caminho_arquivo : str):
    self.cliente : OpenAI = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

Associando arquivos

Agora vamos começar a criar alguns métodos que darão suporte para instanciar o assistente e também associar os arquivos para ele.

Fora do construtor, em uma nova linha, vamos criar um método novo chamado associar_arquivo(). Também será um método de instância, portanto, terá o self. Além disso, terá um caminho_arquivo que passaremos por parâmetro para ele.

Dentro desse método, vamos ter uma estrutura que permite que associemos um arquivo para a OpenAI com o seguinte trecho de código:

def associar_arquivo(self, caminho_arquivo : str):
    novo_arquivo = self.cliente.files.create(
                    file=open(caminho_arquivo, "rb"),
                    purpose="assistants"
            )
    return novo_arquivo.id

Dentro desse método, estamos criando um novo arquivo, armazenado em novo_arquivo. Será usado o self.cliente que criamos no construtor. Esse cliente vai acessar os arquivos, através de files, e vai chamar um endpoint da OpenAI.

O endpoint create() precisa de dois parâmetros. Um deles é o file para o qual passamos o arquivo que iremos abrir. Para isso, usamos o open(), passando o caminho_arquivo e a diretriz rb, para que esse arquivo, que pode ser ou não composto por bytes, seja lido.

Só que a OpenAI também sugere que entreguemos para ela o propósito de por que ela vai inserir esse arquivo na base de dados dela, através do parâmetro purpose.

Por dois motivos. Primeiro, porque depende do propósito, esse arquivo fica explícito, e outras pessoas na organização podem interagir com ele. Segundo, para garantir que esse propósito seja direcionado para um assistente ou para um treinamento. Afinal, alguns dados não podem ser usados para treinamento.

Nesse caso em específico é para um assistente, portanto, colocamos assistants entre aspas.

Por fim, vamos retornar o id desse novo_arquivo que foi associado à OpenAI. Então, ela nos trará uma resposta, da qual vamos acessar o atributo id, que é uma string. Assim, vamos devolver a string para quem precisar dessa informação.

E aí, no construtor, vamos criar um novo atributo, que é o id_arquivo que será do tipo string. Ele virá do método self.associar_arquivo(), onde passaremos por parâmetro o caminho do arquivo que foi associado. Por isso passamos o parâmetro caminho_arquivo igual a caminho_arquivo.

def __init__(self, nome : str, instrucoes : str, caminho_arquivo : str):
    self.cliente : OpenAI = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    self.id_arquivo : str = self.associar_arquivo(caminho_arquivo=caminho_arquivo)

Criando assistente

Agora que já temos o arquivo associado, vamos criar o assistente. Para isso, no final do arquivo, vamos criar um método novo chamado criar_assistente() para garantir que tenhamos um assistente na OpenAI que nos ajudará a responder as nossas dúvidas e desafios.

Ele terá alguns elementos como parâmetro. O self para garantir que é um método de instância, o nome do assistente, as instrucoes para o assistente enviadas através do construtor.

Também terá um modelo que vamos entregar para o assistente. Vamos usar como parâmetro default, para caso esse modelo não esteja informado, o modelo mais recente que temos na OpenAI atualmente, que é o gpt-4o.

Esse é um modelo mais rápido e barato, pois custa metade do preço que o modelo do GPT-4 custaria para podermos enviar e receber informações.

Por último, vamos passar também o ID do arquivo, file_id. Caso não tenha arquivo nenhum, vamos deixar como parâmetro default uma string vazia.

def criar_assistente(self, nome : str, instrucoes : str, modelo = "gpt-4o", file_id = ""):

Feito isso, temos a assinatura do método para criar um assistente. Para isso, vamos criar um novo_assistente que recebe o self.cliente da OpenAI. Desse cliente, vamos usar agora um recurso que ainda está em beta na camada assistants com o endpoint create(). Dessa forma, estamos criando um assistente novo.

O assistente pede alguns parâmetros, o primeiro deles é o name para o qual vamos passar o nome que virá como parâmetro dessa função. Também adicionaremos o parâmetro instructions, que são as nossas instrucoes.

Em seguida, informaremos o modelo dentro do atributo model, que é o que veio por parâmetro, o gpt-4o.

Agora, vamos passar também dois parâmetros diferentes: o primeiro será uma lista de ferramentas através de tools. É importante abrir e fechar colchetes para indicar uma lista.

Cada ferramenta dessa lista será trazida através do formato de um dicionário, isto é, entre chaves. Nele, teremos o tipo da ferramenta, que é a chave do dicionário, descrita como type. Nesse caso, seu valor será a ferramenta de interpretação de código, o code_interpreter.

O próximo parâmetro, depois das ferramentas, envolve os arquivos que vamos enviar para a OpenAI. Por isso, vamos acessar uma camada nova que chamamos de tool_resources - afinal de contas o arquivo é um recurso - e passar um dicionário para ele.

O primeiro recurso será para o interpretador de códigos. A partir de agora, o code_interpreter vai usar todos os arquivos, ou seja, todos os file_ids que estiverem nessa lista. Nesse momento, é uma lista composta pelo único file_id que vem por parâmetro.

Nesse caso, durante o curso, sempre passaremos um arquivo por vez para o assistente.

Finalmente, vamos retornar o assistente da função, que é o novo_assistente.

def criar_assistente(self, nome : str, instrucoes : str, modelo = "gpt-4o", file_id = ""):
    novo_assistente = self.cliente.beta.assistants.create(
        name=nome,
        instructions=instrucoes,
        model=modelo,
        tools=[
            {
                "type":"code_interpreter"
            }  
        ],
        tool_resources={
            "code_interpreter":{
                "file_ids":[file_id]
            }
        }
    )
    return novo_assistente

Feito isso, voltando ao construtor no método __init__(), vamos criar um novo atributo, que será o atributo de assistente.

Esse self.assistente será do tipo Assistant e vai receber o resultado do método self.criar_assistente().

É importante passar os parâmetros necessários para sua criação, começando pelo nome que vai receber o nome que veio por parâmetro. Depois, teremos as instrucoes, que virão das instrucoes que vieram por parâmetro.

Não é necessário passar um modelo, pois tem uma variável default. Se não o informamos, ele associa a esse modelo padrão.

Logo, seguimos para file_id que virá da variável self.id_arquivo criado no construtor.

def __init__(self, nome : str, instrucoes : str, caminho_arquivo : str):
    self.cliente : OpenAI = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    self.id_arquivo : str = self.associar_arquivo(caminho_arquivo=caminho_arquivo)
    self.assistente : Assistant = self.criar_assistente(nome=nome, instrucoes=instrucoes, file_id=self.id_arquivo)

Criando janela de contexto

Para poder ter uma conversa entre o código desenvolvido em Python e os assistentes da OpenAI, precisamos de uma janela de contexto, o qual chamamos de thread.

Então, precisamos criar uma thread para poder associá-la à conversa com a OpenAI. Desse modo, é possível garantir uma janela onde salvaremos todo o fluxo de informações e interações entre o que perguntamos e as respostas que a OpenAI traz.

No final do código, vamos definir um método novo para criar uma thread chamado criar_thread(). Ele não vai receber nenhum parâmetro - a não ser o self para ser um método de instância.

Esse método vai nos retornar uma thread da OpenAI. Para isso, vamos retornar o self.cliente da OpenAI para acessar a parte de recursos beta.threads e usar o endpoint de create(). Assim, vamos retornar uma instância de uma thread para utilizar no assistente de base.

def criar_thread(self):
    return self.cliente.beta.threads.create()

Podemos copiar a assinatura desse método e voltar para o construtor. Nele, criar agora a thread através do self.thread.

Ela será uma Thread da OpenAI e seu resultado virá através da invocação do método self.criar_thread(), que é o método de instância, sem nenhum parâmetro.

O self não vai ser enviado, porque ele apenas garante que seja um objeto com método de instância. Então, o self passa uma autorreferência e não fica explícito na invocação.

def __init__(self, nome : str, instrucoes : str, caminho_arquivo : str):
    self.cliente : OpenAI = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
    self.id_arquivo : str = self.associar_arquivo(caminho_arquivo=caminho_arquivo)
    self.assistente : Assistant = self.criar_assistente(nome=nome, instrucoes=instrucoes, file_id=self.id_arquivo)
    self.thread : Thread = self.criar_thread()

Apagando os recursos

Feito isso, para terminar o assistente de base precisamos apagá-lo, caso não o utilizemos mais - senão os assistentes, os arquivos e as threads ficam no nosso dashboard da OpenAI. Isso pode gerar uma poluição para a organização que você trabalha.

No final do arquivo, vamos finalizar o AssistenteBase com a criação de três métodos de instância que podem ser utilizados para poder apagar os nossos recursos.

Primeiro, vamos começar apagando o elemento mais externo que é o arquivo. Para isso, vamos criar um método chamado apagar_arquivo_openai() que recebe uma instância de objeto para poder ser um método de instância.

Dentro dele, chamamos o self.cliente.files e um endpoint de delete(), passando o próprio self.id_arquivo que armazenamos no assistente.

def apagar_arquivo_openai(self):
    self.cliente.files.delete(self.id_arquivo)

Em sequência, vamos criar mais um método para apagar_thread(), passando apenas self.

Ele pega uma resposta da OpenAI seguindo o mesmo endpoint, chamando o objeto, que chama o cliente, que chama os recursos beta e as threads para deletá-las através do ID associado.

Por fim, devolve a resposta, caso seja preciso manipulá-la para levar essa informação a pessoa usuária.

def apagar_thread(self):
    resposta = self.cliente.beta.threads.delete(self.thread.id)
    return resposta

Por último, vamos apagar o assistente também com o método de apagar_assistente(self). Dentro dela, vamos acessar o endpoint delete() que faz parte dos assistants que está dentro do recurso self.cliente.beta. Devemos passar o ID do assistente como parâmetro e também devolver a resposta.

def apagar_assistente(self):
    resposta = self.cliente.beta.assistants.delete(self.assistente.id)
    return resposta

Conclusão

Dessa forma, desenvolvemos a classe de AssistenteBase.

Ela ainda vai passar por algumas transformações ao longo das nossas aulas, mas é importante entender que ela vai ser utilizada para poder criar qualquer tipo de assistente que vai interagir com a OpenAI.

Até o próximo vídeo.

Sobre o curso GPT Code Interpreter e Python: crie assistentes e aumente sua produtividade

O curso GPT Code Interpreter e Python: crie assistentes e aumente sua produtividade possui 117 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de IA para Programação em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Programação acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas