Tecnologia além do código.
Régys Borges da Silveira
← Voltar para explorar os artigos

Delphi na Era da IA – Implementando Clientes REST para OpenAI e Gemini

delphi na era da ia
Neste artigo 12 tópicos

A integração de modelos de linguagem (LLMs) em aplicações desktop deixou de ser uma novidade para se tornar um requisito de competitividade. No ecossistema Delphi, a biblioteca REST Client Library é a ferramenta nativa mais robusta para essa tarefa.

Embora existam bibliotecas de terceiros, compreender como manipular diretamente TRESTClient, TRESTRequest e TRESTResponse oferece ao desenvolvedor controle total sobre timeouts, manipulação de headers, segurança e tratamento de erros — aspectos cruciais em aplicações de produção.

Este artigo demonstra como construir uma camada de abstração para enviar prompts e processar respostas JSON da OpenAI (GPT) e Google (Gemini), exibindo o resultado formatado.

A Arquitetura REST no Delphi

Para consumir estas APIs, precisamos orquestrar três componentes principais. No Delphi 13, eles receberam otimizações de performance e melhor suporte a HTTPS (TLS 1.3).

  1. TRESTClient: O “gerente” da conexão. Ele define a BaseURL (ex: https://api.openai.com) e gerencia a autenticação global.
  2. TRESTRequest: Representa uma chamada específica (Endpoint). Aqui definimos o método (POST), o recurso (/v1/chat/completions) e o Payload (Corpo do JSON).
  3. TRESTResponse: O container que recebe os dados brutos, headers de resposta e o código de status HTTP.

Implementação Prática

Vamos criar um método encapsulado que possa ser reutilizado. Imagine um formulário com um TMemo para o prompt (input), um TRichEdit para a resposta e um botão para enviar.

Configuração dos Componentes (Runtime vs Design Time)

Para usuários avançados, instanciar componentes em runtime é preferível para manter o código limpo e evitar o “clutter” no DataModule, além de facilitar o controle de ciclo de vida (try-finally).

Você precisará das seguintes unidades na cláusula uses:

uses
  System.SysUtils, System.Classes, System.JSON, REST.Client, REST.Types, 
  Data.Bind.Components, Data.Bind.ObjectScope;

O Código Core de Consumo

Abaixo, apresento uma função robusta que configura a chamada, serializa o JSON de envio e deserializa a resposta.

Nota: O exemplo utiliza a estrutura da OpenAI, mas abordaremos a estrutura do Gemini na seção 4.

procedure TFormMain.EnviarPromptParaIA(const APrompt: string; const AApiKey: string);
var
  LClient: TRESTClient;
  LRequest: TRESTRequest;
  LResponse: TRESTResponse;
  LJsonBody: TJSONObject;
  LMessages: TJSONArray;
  LMessageObj: TJSONObject;
  LResponseJson: TJSONValue;
  LContent: string;
begin
  // Instanciação dos componentes REST
  LClient := TRESTClient.Create(nil);
  LRequest := TRESTRequest.Create(nil);
  LResponse := TRESTResponse.Create(nil);
  
  // Construção do JSON de Envio (Payload)
  // Estrutura: { "model": "gpt-4", "messages": [{"role": "user", "content": "..."}] }
  LJsonBody := TJSONObject.Create;
  try
    LJsonBody.AddPair('model', 'gpt-4o-mini'); // Ou o modelo de sua preferência
    
    LMessages := TJSONArray.Create;
    LMessageObj := TJSONObject.Create;
    LMessageObj.AddPair('role', 'user');
    LMessageObj.AddPair('content', APrompt);
    LMessages.AddElement(LMessageObj);
    
    LJsonBody.AddPair('messages', LMessages);

    // Configuração do Cliente
    LClient.BaseURL := 'https://api.openai.com/v1';
    LClient.ContentType := 'application/json';
    
    // Configuração do Request
    LRequest.Client := LClient;
    LRequest.Response := LResponse;
    LRequest.Resource := 'chat/completions';
    LRequest.Method := TRESTRequestMethod.rmPOST;
    
    // Autenticação (Bearer Token)
    LRequest.Params.AddHeader('Authorization', 'Bearer ' + AApiKey);
    
    // Anexando o corpo JSON. O parâmetro 'True' define que o Request é dono do objeto e o liberará.
    LRequest.AddBody(LJsonBody, True); 

    try
      // Execução (Síncrona para simplificação, ver seção Avançada para Assíncrona)
      LRequest.Execute;

      // Verificação de Status HTTP (200 OK)
      if LResponse.StatusCode = 200 then
      begin
        // Parse da Resposta
        LResponseJson := LResponse.JSONValue;
        if Assigned(LResponseJson) then
        begin
          // Navegação no JSON: choices[0].message.content
          // Utilizamos TryGet para evitar Access Violations em estruturas inesperadas
          if LResponseJson.TryGetValue<string>('choices[0].message.content', LContent) then
          begin
            // Atualiza a UI (Thread Safe se fosse async)
            RichEditResposta.Lines.Clear;
            RichEditResposta.Lines.Add(LContent);
          end;
        end;
      end
      else
      begin
        RichEditResposta.Lines.Add('Erro na API: ' + LResponse.StatusText);
        RichEditResposta.Lines.Add('Detalhe: ' + LResponse.Content);
      end;

    except
      on E: Exception do
        RichEditResposta.Lines.Add('Erro de Exceção: ' + E.Message);
    end;

  finally
    // Liberação de memória
    // Nota: LJsonBody foi liberado pelo LRequest.AddBody(..., True)
    LResponse.Free;
    LRequest.Free;
    LClient.Free;
  end;
end;

Diferenças Cruciais: OpenAI vs. Gemini

Embora o conceito seja idêntico, o Endpoint e o Payload JSON diferem. Se você estiver usando a API do Google Gemini, ajuste os seguintes pontos no código acima:

URL e Resource

  • BaseURL: https://generativelanguage.googleapis.com/v1beta
  • Resource:models/gemini-1.5-flash:generateContent?key=SUA_API_KEY
    • Nota: O Google costuma passar a API Key na URL (Query Parameter) ao invés do Header Authorization: Bearer (embora suporte ambos em alguns contextos).

Estrutura do JSON (Gemini)

O Gemini utiliza uma estrutura baseada em contents e parts:

{
  "contents": [{
    "parts": [{"text": "Sua pergunta aqui"}]
  }]
}

Adaptação no Delphi:

// Construção para Gemini
LJsonBody.AddPair('contents', TJSONArray.Create(
  TJSONObject.Create(
    TJSONPair.Create('parts', TJSONArray.Create(
      TJSONObject.Create(
        TJSONPair.Create('text', APrompt)
      )
    ))
  )
));

Caminho de Leitura (Parsing): O caminho para extrair a resposta no Gemini é: candidates[0].content.parts[0].text.

Tópicos Avançados: Performance e UI

Serialização e Deserialização

O uso de TryGetValue<T> (demonstrado no código) é a forma moderna e segura de ler JSON no Delphi 13. Evite o uso excessivo de LResponse.JSONValue.FindValue('path') antigos, pois eles exigem type casting manual e são propensos a erros se o nó for nil.

Não congele a interface (ExecuteAsync)

Para uma aplicação profissional, jamais use LRequest.Execute na thread principal (MainThread), pois isso trava a aplicação até a resposta da IA.

O TRESTRequest possui um método nativo para isso:

LRequest.ExecuteAsync(
  procedure
  begin
    // Este código roda quando a requisição termina
    if LResponse.StatusCode = 200 then
    begin
      // Parsing do JSON aqui...
      // Atualização da UI
      TThread.Synchronize(nil, procedure
      begin
        RichEditResposta.Lines.Text := ConteudoExtraido;
      end);
    end;
    
    // Importante: Liberar componentes criados em runtime aqui dentro ou gerenciar ciclo de vida
  end,
  True, // FreeThreadOnTermination
  True  // SynchronizeOnSuccess (Se True, o callback roda na MainThread, dispensando o TThread.Synchronize interno)
);

Exibição em TRichEdit

O texto retornado pelas IAs geralmente vem em formato Markdown (com negritos **texto**, blocos de código, etc.). O TRichEdit nativo do Delphi não renderiza Markdown automaticamente.

  • Solução Simples: Jogar o texto puro (Plain Text).
  • Solução Avançada: Utilizar uma biblioteca de parser Markdown para Delphi ou processar manualmente tags simples (ex: substituir ** por atributos de fonte fsBold) antes de inserir no RichEdit.

Conclusão

Com o Delphi, o consumo de APIs REST para IA é direto e tipado. O segredo para uma aplicação robusta não está apenas no envio do request, mas no tratamento elegante do JSON de resposta e na gestão assíncrona das chamadas para manter a fluidez da experiência do usuário.

Próximo Passo: Tente implementar o modo “Streaming” (Server-Sent Events), onde a resposta da IA aparece letra por letra na tela, utilizando o evento OnReqData do TRESTClient.

Compartilhar por e-mail

0 comentário(s)

Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

Um próximo passo para cada interesse.

Continue estudando com os livros, acompanhe as videoaulas ou consulte as soluções para o seu projeto.

Descubra mais sobre Régys Borges da Silveira

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo