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).
- TRESTClient: O “gerente” da conexão. Ele define a
BaseURL(ex:https://api.openai.com) e gerencia a autenticação global. - TRESTRequest: Representa uma chamada específica (Endpoint). Aqui definimos o método (POST), o recurso (
/v1/chat/completions) e o Payload (Corpo do JSON). - 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).
- Nota: O Google costuma passar a API Key na URL (Query Parameter) ao invés do Header
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 fontefsBold) 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.
Descubra mais sobre Régys Borges da Silveira
Assine para receber nossas notícias mais recentes por e-mail.
Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.