Delphi 如何调用OpenAI的API接口,此处只描述API调用!

浏览OpenAI的API接口文档,其实OpenAI的接入非常简单,只要有API_KEY就可以动手开发了(前提要会魔法)。他本身就是一个HTTP的POST和GET请求,以下我是从零开始接入接口的不同阶段和相关实现。

第一步,可能很多人都和我一样,关于HTTP的接口,首先使用的都是POSTMAN工具。
在这里插入图片描述
header这里,填写和修改这两个位置就可以了,Authorization就是你申请到的key,切记key前边必须要加Bearer 。
在这里插入图片描述
请求数据是json结构,按照官网的要求就可以,我只填写了我需要使用的。做完这一切很轻松就完成了接口调用并得到结果。

第二步,已经知道如何调用接口,那么剩下啦就是如何用delphi代码实现。我想很多人也一样首先想到的控件就是Idhttp。所以我也是先用这个控件实现。

//引用单元
uses System.NetEncoding;
//代码片段
procedure TForm1.Button2Click(Sender: TObject);
var
  IdHTTP: TIdHTTP;
  SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
  RequestBody, ResponseBody: TStringStream;
  AccessToken: string;
begin
  IdHTTP := TIdHTTP.Create(nil);
  SSLIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  RequestBody := TStringStream.Create
    ('{"model": "gpt-3.5-turbo","messages": [{"role": "user", "content": "重复一次这是个测试"}],"temperature": 0.8}',
    TEncoding.UTF8);
  ResponseBody := TStringStream.Create('', TEncoding.UTF8);
  try
    // 设置 SSLIOHandler
    SSLIOHandler.SSLOptions.Method := sslvTLSv1_2;
    SSLIOHandler.SSLOptions.Mode := sslmClient;
    SSLIOHandler.SSLOptions.VerifyMode := [];
    SSLIOHandler.SSLOptions.VerifyDepth := 0;
    IdHTTP.IOHandler := SSLIOHandler;
    // 设置 IdHTTP
    IdHTTP.Request.Accept := 'application/json';
    IdHTTP.Request.ContentType := 'application/json';
    // IdHTTP.Request.BasicAuthentication := False;
    IdHTTP.Request.CustomHeaders.Values['User-Agent'] :=
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36';
    // 设置请求参数
    IdHTTP.Request.CustomHeaders.Values['Authorization'] :=
    'Bearer 这里填写你的API的KEY';
    // 发送请求并获取响应
    IdHTTP.Post('https://api.openai.com/v1/chat/completions', RequestBody,
      ResponseBody);
    // 解析响应数据

    // Memo1.Text := ResponseBody.DataString;
    // 处理身份验证令牌
    // ...
  finally
    IdHTTP.Free;
    SSLIOHandler.Free;
    RequestBody.Free;
    ResponseBody.Free;
  end;
end;

这样就简单的实现了OpenAI的API接入,并可以与chatGpt沟通了。可是发现一个问题,这样实现不能像官网一样,逐字回复。只能等到全部回复完成,才会显示内容。那就让体验很不好。可是我试了很多次,可能是我对IDHTTP了解不够深入,我没有办法实现异步接收。这时候我想起了另一个控件,那就是NetHTTPClient。

第三步,那就用NetHTTPClient实现异步方式接收chatGpt的回复,实现与官网一致的逐字显示回复内容。

//首先实现发送数据
procedure TForm1.Button1Click(Sender: TObject);
var
  RequestBody, ResponseBody: TStringStream;
begin
  // "stream":true, JSON结构里添加这个参数,会让接口逐字推送 
  RequestBody := TStringStream.Create 
    ('{"model": "gpt-3.5-turbo","messages": [{"role": "user", "content": "说出10个一千零一夜的故事"}],"stream":true,"temperature": 0.8}',
    TEncoding.UTF8);
  ResponseBody := TStringStream.Create('', TEncoding.UTF8);
  //开启异步
  HttpClient.Asynchronous := true;
  HttpClient.Accept := 'application/json';
  HttpClient.ContentType := 'application/json';
  // IdHTTP.Request.BasicAuthentication := False;
  HttpClient.CustomHeaders['User-Agent'] :=
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36';
  // 设置请求参数
  HttpClient.CustomHeaders['Authorization'] :='Bearer 这里填写你的API的KEY';
  Memo1.Clear;
  Memo1.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', now()) + '==>user');
  Memo1.Lines.Add('说出10个一千零一夜的故事');
 //这里有三个声明,放在单元的private里 rlength: integer;lstr: string;Response: IHTTPResponse;
  rlength := 0;
  lstr := '';
  Response := HttpClient.Post('https://api.openai.com/v1/chat/completions',
    RequestBody, ResponseBody);
end;
//这里实现异步接收数据,并把返回的数据显示在memo里。
//使用的NetHTTPClient的onReceiveData事件
procedure TForm1.HttpClientReceiveData(const Sender: TObject;
  AContentLength, AReadCount: Int64; var AAbort: Boolean);
var
  i: integer;
  s, jstr, cstr: string;
  jsonArr: TJSONArray;
begin
  if Response.StatusCode = 200 then
  begin
    s := Response.ContentAsString(TEncoding.UTF8);
    jstr := copy(s, rlength, length(s));
    jstr := StringReplace(jstr, #$A#$A, '', [rfReplaceAll]);
    if jstr.Trim <> '' then
    begin
      jsonArr := TJSONArray.Create;
      try
        //这个函数下边会写上代码,因返回不是标准JSON数据,前边带了data:,所以做字符串截取     
        getOpenAIdb(jstr, jsonArr);
        for i := 0 to jsonArr.Count - 1 do
        begin
          // Memo1.Lines.Add();
          lstr := lstr + jsonArr.Get(i).Value;
          //getOpenAI_R这个函数下边会写上代码,获取消息的第一条,采用的也是字符串截取
          cstr := getOpenAI_R(jsonArr.Get(i).Value).Trim;
          if cstr <> '' then
          begin
            Memo1.Lines.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss:zzz', now()) +
              '==>' + cstr);
          end;
          //getOpenAI_C这个函数下边会写上代码,获取消息的内容,采用的也是字符串截取
          cstr := getOpenAI_C(jsonArr.Get(i).Value).Trim;
          if cstr <> '' then
          begin
            if cstr = '\n' then
              Memo1.Text := Memo1.Text + #13#10
            else
              Memo1.Text := Memo1.Text + cstr;
          end;
        end;
      finally
        FreeAndNil(jsonArr);
      end;
    end;
    rlength := length(s);
  end;
end;

这里做了很对字符串的截取操作,原因就是返回的不是标准json结构,就算截取出json结构,想取到里面的内容也很深,相对很麻烦。但是考虑也简单的pos和copy来实现,发现一样麻烦,而且可能会取的不够准确。所以直接使用大招,就是问chatGpt有没有简单的办法。
在这里插入图片描述
说实话,让我自己想一辈子都想不到正则表达式库,满脑子都是不停循环的pos和copy。这样一个反馈,瞬间灵感爆发,如下是那三个截取字符串的代码

//首先引用正则库的单元uses System.RegularExpressions;
//这个大家自己去优化,这是截取上一个data:与下一个data:之间的json字符串部分
procedure TForm1.getOpenAIdb(instr: string; outarray: TJSONArray);
var
  s: string;
  RegEx: TRegEx;
  Match: TMatch;
begin
  RegEx := TRegEx.Create('data:\s*(.*?)(?=data:|$)');
  Match := RegEx.Match(instr);
  while Match.Success do
  begin
    s := Trim(Match.Groups[1].Value);
    outarray.Add(s);
    Match := Match.NextMatch;
  end;
end;
//这是获取第一条回复数据,里面有role。
function TForm1.getOpenAI_R(instr: string): string;
var
  RegEx: TRegEx;
  Match: TMatch;
begin
  Result := '';
  RegEx := TRegEx.Create('"role"\s*:\s*"([^"]+)"');
  Match := RegEx.Match(instr);
  if Match.Success then
  begin
    Result := Match.Groups[1].Value;
  end;
end;
//这里是获取content,文本内容
function TForm1.getOpenAI_C(instr: string): string;
var
  RegEx: TRegEx;
  Match: TMatch;
begin
  // 查找 "content"
  Result := '';
  RegEx := TRegEx.Create('"content"\s*:\s*"([^"]+)"');
  Match := RegEx.Match(instr);
  if Match.Success then
  begin
    Result := Match.Groups[1].Value;
  end;
end;

在这里插入图片描述
最后简单的接口实现就完成了,实现效果与官网的基本相同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值