Com a crescente demanda por soluções inteligentes, integrar assistentes de IA em aplicações tornou-se uma oportunidade valiosa para desenvolvedores. Para isso, a OpenAI ampliou suas capacidades com a API Assistants, permitindo a criação de assistentes personalizados com funcionalidades avançadas. Neste artigo, exploraremos como utilizar essa API, desde a configuração inicial até exemplos práticos.
A API Assistants e a API tradicional do ChatGPT compartilham a base tecnológica dos modelos avançados da OpenAI, mas possuem abordagens distintas quanto à funcionalidade e ao uso:
Personalização e instruções específicas:
Threads persistentes:
Manipulação de arquivos:
Para fins didáticos, utilizaremos aqui uma aplicação do tipo Console. Então, para começar, vamos organizar a estrutura básica da nossa aplicação de forma clara:
public class Program
{
private static readonly string apiKey = ""; // Substitua com sua chave de API
private static readonly string apiUrl = "https://api.openai.com/v1";
private static string assistantId;
private static string threadId;
public static async Task Main(string[] args)
{
try
{
//Implementação do nosso código.
}
catch (Exception ex)
{
Console.WriteLine($"Erro geral: {ex.Message}");
}
}
private static async Task CriarAssistente()
{
}
private static async Task CriarThread()
{
}
private static async Task EnviarMensagem(string assistantId, string threadId, string Mensagem)
{
}
private static async Task BuscarMensagens(string threadId)
{
}
}
Dentro da classe “Program” nós criamos quatro variáveis. Na variável “apiKey” deve ser armazenada a chave de API da OpenAI. Caso tenha dúvidas de como conseguir essa chave, você pode ver o passo a passo neste tutorial.
A URL base (apiUrl) é fixa e aponta para o endpoint de interações com assistentes. A variável “assistantId” vai armazenar o id da nossa assistente e a “threadId” vai armazenar o id da thead que vamos criar.
O método principal será a “Main”, responsável por iniciar a execução do sistema e controlar o fluxo principal da interação com a API da OpenAI.
Além dela, contaremos com quatro métodos que desempenham funções específicas:
A primeira etapa para interagir com a API do OpenAI é criar um assistente. Vamos definir algumas configurações iniciais no método “CriarAssistente”:
private static async Task CriarAssistente()
{
using var client = new HttpClient();
var data = new
{
instructions = "Você é um professor de matemática que auxilia seus alunos.",
name = "Professor",
tools = new List
Aqui nós criamos uma instância de “HttpClient” para realizarmos as requisições. Após isso, montamos o objeto da nossa requisição que vai conter a instrução do que nossa assistente deve fazer, o seu nome, uma lista de ferramentas que o assistente pode usar como pesquisa de arquivos e interpretação de código. E por fim, o modelo que usaremos.
Ainda dentro do método “CriarAssistente”, vamos montar a nossa requisição e envia-la:
var request = new HttpRequestMessage(HttpMethod.Post, $"{apiUrl}/assistants")
{
Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8,
"application/json")
};
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("OpenAI-Beta", "assistants=v2");
var response = await client.SendAsync(request);
Após realizar o envio da requisição, vamos ler a informação que foi retornada:
var result = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(result);
return doc.RootElement.GetProperty("id").GetString();
No código acima, após realizarmos a leitura da nossa resposta, vamos armazená-la em “result”. Após isso, vamos transformar o valor recebido em um JSON para permitir o acesso estruturado aos seus dados. O código então vai navegar até a raiz do documento (RootElement) e extrair o valor associado à propriedade “id”, convertendo-o em uma string com “GetString()”. Com isso, conseguimos capturar o id da nossa assistente.
Após criar o assistente, precisamos de uma “thread” (ou conversa) onde as interações acontecerão.
private static async Task CriarThread()
{
using var client = new HttpClient();
var data = new { metadata = new { project = "AulasMatematica" } };
//Aqui o objeto “data” vai conter as informações do projeto que você está criando.
var request = new HttpRequestMessage(HttpMethod.Post, $"{apiUrl}/threads")
{
Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json")
};
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("OpenAI-Beta", "assistants=v2");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(result);
return doc.RootElement.GetProperty("id").GetString();
}
O código acima vai seguir o mesmo fluxo da criação da assistente, retornando o id da thread que foi criada.
Agora que temos o assistente e a thread criados, podemos enviar mensagens para o assistente da seguinte forma:
private static async Task SendMessageAsync(string assistantId, string threadId, string mensagem)
{
using var client = new HttpClient();
var data = new
{
assistant_id = assistantId,
instructions = "Responda apenas às perguntas referentes a matemática. Caso seja falado sobre outros assuntos, responda educadamente para voltar para assuntos relacionados a matemática.",
additional_messages = new[] { new { role = "user", content = mensagem} }
};
//...continua
}
Aqui, a variável “data” guarda o “assistantId” do assistente que foi criado, junto com as instruções específicas para essa conversa que foi armazenado na propriedade “instructions”.
Uma dica importante: durante o teste desta API ficou perceptível que apenas essas instruções são levadas em conta para influenciar as respostas. Então a indicação que fica é de adicionar as instruções detalhadas nesta propriedade.
Por fim, é adicionado uma mensagem que contém duas propriedades. A primeira é quem está enviando a pergunta, que neste caso é o “user” e o segundo campo e a mensagem que foi digitada.
Após isso, é seguido o fluxo de envio da requisição.
var request = new HttpRequestMessage(HttpMethod.Post,
$"{apiUrl}/threads/{threadId}/runs")
{
Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json")
};
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("OpenAI-Beta", "assistants=v2");
var response = await client.SendAsync(request);
Logo abaixo, nós vamos implementar a busca das mensagens da nossa thead, incluindo a resposta da pergunta que foi feita. E para podermos manipular esta resposta nós vamos desserializar a resposta recebida para um objeto C#. Então, em um arquivo separado adicione o seguinte código:
public class MessageList
{
[JsonPropertyName("data")]
public List Data { get; set; }
}
public class Message
{
[JsonPropertyName("role")]
public string Role { get; set; }
[JsonPropertyName("content")]
public List Content { get; set; }
}
public class Content
{
[JsonPropertyName("text")]
public Text Text { get; set; }
}
public class Text
{
[JsonPropertyName("value")]
public string Value { get; set; }
}
O JSON retornado possui diversos outros campos, mas nos objetos acima nós vamos capturar apenas as informações que são necessárias para processarmos a nossa resposta.
Agora vamos implementar uma função para receber as mensagens do assistente. Ela recupera a lista de mensagens da thread e as exibe no console.
private static async Task BuscarMensagens(string threadId)
{
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, $"{apiUrl}/threads/{threadId}/messages");
request.Headers.Add("Authorization", $"Bearer {apiKey}");
request.Headers.Add("OpenAI-Beta", "assistants=v2");
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var messageList = JsonSerializer.Deserialize(result);
Console.Clear();
if (messageList != null && messageList.Data != null)
{
messageList.Data.Reverse();
foreach (var message in messageList.Data)
{
var contentText = message.Content[0]?.Text?.Value;
if (!string.IsNullOrEmpty(contentText))
{
Console.WriteLine($"[{message.Role.ToUpper()}]: {contentText}");
}
}
}
}
No código acima, nós vamos realizar a requisição para o endpoint que recebe todas as mensagens da thead. Após isso, a resposta será desserealizada para o objeto C# que criamos anteriormente.
Em seguida, vamos limpar o nosso console, verificar se o objeto é nulo, reverter a lista para mostrar as mensagem da mais antiga para a mais recente e por fim, percorremos essa lista de mensagens apresentando na tela quem escreveu a mensagem (user ou assistent) e a mensagem.
Agora que implementamos as quatro ações da API, vamos para a “Main” para criarmos o fluxo de interação com o usuário. O usuário pode digitar mensagens que serão enviadas ao assistente, e as respostas serão exibidas na tela.
public static async Task Main(string[] args)
{
try
{
assistantId = await CriarAssistente();
threadId = await CriarThread();
Console.WriteLine("Assistente criado com ID: " + assistantId);
Console.WriteLine("Thread criada com ID: " + threadId);
No código acima, quando iniciamos a nossa aplicação, ela primeiro acessa o método “CriarAssistente()” e salva o ID retornado na variável “assistantId”, que criamos logo no início. Esse ID representa o assistente que será utilizado nas próximas operações.
Na sequência, o programa chama o método “CriarThread()” para criar uma nova thread de comunicação. O ID gerado por esse processo é armazenado na variável “threadId”, que também declaramos anteriormente.
Depois disso, exibimos no console os IDs do assistente e da thread para confirmar que eles foram criados com sucesso.
bool continuar = true;
while (continuar)
{
Console.Write("\nDigite sua mensagem (ou 'sair' para encerrar): ");
string userMessage = Console.ReadLine();
if (userMessage?.ToLower() == "sair")
{
continuar = false;
break;
}
await EnviarMensagem(assistantId, threadId, userMessage);
Com o assistente e a thread criados, o programa entra em um loop contínuo para permitir a interação com o usuário.
Dentro desse laço, o usuário digita uma mensagem. Se o texto digitado for “sair”, o programa altera a variável continuar para false, encerrando a execução. Se a mensagem não for “sair”, o programa a envia para o assistente utilizando o método “EnviarMensagem”.
Console.WriteLine("Processando ...");
await Task.Delay(TimeSpan.FromSeconds(5));
await BuscarMensagens(threadId);
}
}
catch (Exception ex)
{
Console.WriteLine($"Erro geral: {ex.Message}");
}
}
Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aqui
Se é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui
Se você chegou até aqui, parabéns por concluir mais esse aprendizado técnico! O próximo passo é continuar testando e ajustando o seu assistente para explorar todo o potencial da inteligência artificial em suas aplicações.