FortuneAI: Utilizando a API da OpenAI em uma aplicação SwiftUI

OpenAI é uma empresa estadunidense de pesquisa em inteligência artificial que desenvolve e promove tecnologias como o ChatGPT, ferramenta que surpreendeu o mundo digital e tem ganhado destaque desde o seu lançamento no final de 2022.

Além de fornecer serviços diretos de geração de texto, tradução e sumarização, a OpenAI também disponibiliza uma API que visa possibilitar a integração direta entre uma aplicação externa e seus modelos de geração de texto e imagem.

Nesse artigo, irei demonstrar como usar essa ferramenta em suas aplicações através de um tutorial simples e lúdico: usando do SwiftUI no iOS, construiremos um vidente que responderá a perguntas do usuário com suporte do ChatGPT.

Construção do Aplicativo

Durante o decorrer desse tutorial, poderemos ter contato com alguns conceitos essenciais na vida de qualquer pessoa desenvolvedora: gerenciamento de pacotes, integração com APIs externas e Engenharia de Software.

O nosso aplicativo (batizado carinhosamente de FortuneAI), terá uma interface simples: uma caixa de entrada de texto para a inserção da pergunta, um botão de "Enviar", um botão de "Resetar" e, por fim, o espaço de texto da resposta. Também adicionaremos um conjunto de 3 imagens do nosso Mago Vidente para tornar a aparência mais agradável.

Estrutura básica da interface

O código base do esqueleto da aplicação ficou dessa maneira:

import SwiftUI

struct ContentView: View {
@State private var question: String = ""
@State private var answer: String = ""
@State private var mageImage: String = "NOME_DA_PRIMEIRA_IMAGEM"

var body: some View {
GeometryReader { _ in
ZStack {
Color("Background").edgesIgnoringSafeArea(.all)

VStack {
HStack {
TextField("Pergunte ao mago", text: $question)
.disableAutocorrection(true)

Button(action: {}) {
Text("🔮")
.padding()
.background(RoundedRectangle(cornerRadius: 8).fill(Color.blue).frame(maxHeight: 36))
.foregroundStyle(.white)
}
.buttonStyle(PlainButtonStyle())

Button(action: {}) {
Text("🔄")
.padding()
.background(RoundedRectangle(cornerRadius: 8).fill(Color.blue).frame(maxHeight: 36))
.foregroundStyle(.white)
}
.buttonStyle(PlainButtonStyle())
}
.padding()

Image(mageImage)
.resizable()
.aspectRatio(contentMode: .fit)
.padding()
.frame(height: 320)

Text(answer)
.padding()
.frame(height: 160)
}
.textFieldStyle(.roundedBorder)
}

}
.ignoresSafeArea(.keyboard)
}

Basta adicionar as imagens e a cor "Background" no .xcassets. Para as imagens do nosso Mago, usaremos as seguintes figuras (que estão disponíveis no repositório encontrado ao final do artigo):

O Mago Vidente antes, durante e depois da sua previsão mística

Gerenciamento de pacotes

Após a finalização do esqueleto visual de nossa aplicação, partiremos para a integração direta com a OpenAI. A primeira coisa que precisaremos fazer é adicionar o pacote responsável por esse gerenciamento no Swift: convenientemente, seu nome também é OpenAI.

Com o pacote adicionado às dependências do projeto, podemos importá-lo ao início de nosso código:

import OpenAI

Nossa aplicação agora está plenamente equipada para lidar com chamadas externas à modelos GPT de geração de texto!

Autenticando nossas requisições

Até a data de publicação desse artigo, a API da OpenAI é paga, o que significa que cada chamada (cada resposta do mago) custará uma pequena fração de centavos de dólar. Felizmente, a OpenAI presenteia novos usuários com um valor de 5 USD, que é mais que suficiente para muitos testes e previsões místicas!

Precisamos de uma chave (que nada mais é do que uma string bem esquisita) para identificar nossa conta em cada chamada da API. O primeiro passo para conseguí-la é, claro, criar uma conta na OpenAI.

Dentro de sua conta, acesse a documentação da API. Aqui se encontram muitos detalhes sobre todos os serviços e funcionalidades disponíveis nessa ferramenta, então vale a pena dar uma explorada!

Na barra lateral esquerda encontraremos várias seções importantes. Focaremos em duas: API Keys e Usage.

Visão geral da barra lateral esquerda e da tela inicial de documentação

Na tela de Usage se encontram as informações financeiras. Aqui podemos monitorar nossos créditos: quantos foram usados, quantos restam e quando os restantes irão expirar.

Vamos agora à tela de API Keys. É aqui que geraremos a chave de uso para nossa aplicação. Crie a chave (e certifique-se de guardá-la num lugar seguro, já que você não poderá recuperá-la caso a perca) e insira-a em sua aplicação dentro da nossa struct ContentView. E claro, substituindo sua chave por onde abaixo está escrito "SUA_CHAVE_AQUI":

struct ContentView: View {
...

let ourOpenAI = OpenAI(apiToken: "SUA_CHAVE_AQUI")

var body: some View {

...

}
}

Com nossa chave de autenticação disponível em nosso código, podemos partir para a troca de mensagens com a OpenAI.

Estabelecendo contato

A partir de agora, estaremos focados em enviar uma pergunta ao Mago e obter uma resposta mágica. A função responsável por essa funcionalidade vai ter o seguinte formato:

func sendQuestion() {

mageImage = "NOME_DA_SEGUNDA_IMAGEM"

let query = ChatQuery(
messages: [.init(
role: .user,
content: "Responda de maneira sucinta, objetiva, direta e mística como se fosse um vidente mágico: " + question)!],
model: .gpt3_5Turbo)

ourOpenAI.chats(query: query) { result in
switch result {
case .success(let chatResult):
let content = chatResult.choices[0].message.content?.string
answer = content!
case .failure(_):
answer = "O mago não pode responder no momento"
}
mageImage = "NOME_DA_TERCEIRA_IMAGEM"
}
}

É, parece algo bem estranho. Vamos analisá-la por partes:

let query = ChatQuery(
messages: [.init(
role: .user,
content: "Responda de maneira sucinta, objetiva, direta e mística como se fosse um vidente mágico: " + question)!],
model: .gpt3_5Turbo)

Primeiro a gente define a nossa query (que é uma maneira comum de chamar uma pergunta, pedido ou requisição). Ela é uma struct do formato ChatQuery (que vem junto com nosso pacote OpenAI importado lá em cima).

Dentro de nossa query, definimos uma mensagem em messages e um modelo em model. A mensagem tem duas partes: um role e um content, que indicam respectivamente quem está enviando a mensagem (o nosso usuário, por isso .user) e qual é o conteúdo da mensagem (a nossa pergunta).

O conteúdo de nossa pergunta é composto da junção de duas strings: uma constante que pede que a resposta seja breve e característica de um vidente; e a pergunta específica de nosso usuário, question.

O modelo model diz respeito a qual modelo do GPT deverá ser usado para fornecer a resposta. O gpt3_5turbo é um bom modelo padrão para geração de texto, mas você pode encontrar várias alternativas na documentação da API (também há modelos para outros propósitos, como geração de imagem).

Seguindo em frente:

ourOpenAI.chats(query: query) { result in
switch result {
case .success(let chatResult):
let content = chatResult.choices[0].message.content?.string
answer = content!
case .failure(_):
answer = "O mago não pode responder no momento"
}
mageImage = "NOME_DA_SEGUNDA_IMAGEM"
}

Aqui nós chamamos o método chats em nossa instância ourOpenAI usando nossa query como argumento. Recebemos a resposta result da OpenAI e avaliamos se a requisição deu certo (case .success()) ou se deu errado (case .failure()). Caso dê errado, nós retornamos uma mensagem ao usuário sem sair da ambientação "mágica". Em caso de sucesso, a resposta é retornada dentro de um objeto desse formato:

{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"content": "SUA RESPOSTA AQUI",
"role": "assistant"
},
"logprobs": null
}
],
"created": 1677664795,
"id": "chatcmpl-7QyqpwdfhqwajicIEznoc6Q47XAyW",
"model": "gpt-3.5-turbo",
"object": "chat.completion",
"usage": {
"completion_tokens": 17,
"prompt_tokens": 57,
"total_tokens": 74
}
}

Por isso acessamos a resposta desejada através da linha:

let content = chatResult.choices[0].message.content?.string

E depois atribuímos ao nosso state answer, finalizando a interação entre o usuário e a API e mostrando a última imagem do Mago. Finalmente, basta criar uma função para reiniciar todos os nossos estados (essa função será a ação realizada pelo nosso botão de reset (também chamado de reload ou refresh) 🔄:

func sendQuestion() {

...

}

func reset() {
mageImage = "NOME_DA_PRIMEIRA_IMAGEM"
answer = ""
question = ""
}

Fim?

Se você quiser dar uma olhada em como ficou o meu resultado final, dá uma olhada aqui. É também sempre bom lembrar: caso for compartilhar seu código, omita a sua chave OpenAI.

Tentei oferecer um ponto de partida simples para a exploração das possibilidades oferecidas pela API da OpenAI e de demais tecnologias de IA generativa. O uso consciente e democratizado desse tipo de tecnologia pode ser uma chave para muitos outros aprendizados, técnicos ou não. Obrigado pela atenção, espero que esse texto tenha sido de alguma ajuda.

The best way to predict the future is to create it
Peter Drucker

--

--