超级详细 JAVA 对接 ChatGPT 教程,实现自己的AI对话小助手

oopo / 2024-01-31 / 原文

1     前言

  大家好,由于近期需要对接了ChatGPT API所以特地记录下来,据介绍该模型是和当前官网使用的相同的模型,如果你还没体验过ChatGPT,那么今天就教大家如何打破网络壁垒,打造一个属于自己的智能助手把。本文包括API Key的申请以及网络代理的搭建,那么事不宜迟,我们现在开始。

  若有想体验的可联系我获取体验账号。

2     对接流程

2.1  API-Key的获取


  首先第一步要获取OpenAI接口的API Key,该Key是你用来调用接口的token,主要用于接口鉴权。获取该key首先要注册OpenAi的账号。

2.1.1      打开platform.openai.com网站,点击view API Key

 

 

2.1.2      点击创建key

 

 

2.1.3      弹窗显示生成的key,记得把key复制,不然等会就找不到这个key了,只能重新创建

 

 

将API Key保存好以备用

2.2  API用量的查看

   这里可以查看API的使用情况,新账号注册默认有5美元的试用额度,之前都是18美元,API成本降了之后试用额度也狠狠地砍了一刀。

 

 

2.3  核心代码实现

2.3.1      pom依赖

  其中引入包cdkj-core请参考另一开源项目 维基框架

  1.  
    <?xml version="1.0" encoding="UTF-8"?>
  2.  
    <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.  
    <parent>
  6.  
    <artifactId>framewiki-gpt</artifactId>
  7.  
    <groupId>com.framewiki.gpt</groupId>
  8.  
    <version>1.0.0</version>
  9.  
    </parent>
  10.  
    <modelVersion>4.0.0</modelVersion>
  11.  
     
  12.  
    <artifactId>gpt-util</artifactId>
  13.  
     
  14.  
    <dependencies>
  15.  
    <dependency>
  16.  
    <groupId>com.cdkjframework</groupId>
  17.  
    <artifactId>cdkj-core</artifactId>
  18.  
    </dependency>
  19.  
    <dependency>
  20.  
    <groupId>com.cdkjframework</groupId>
  21.  
    <artifactId>cdkj-util</artifactId>
  22.  
    </dependency>
  23.  
    <dependency>
  24.  
    <groupId>commons-httpclient</groupId>
  25.  
    <artifactId>commons-httpclient</artifactId>
  26.  
    </dependency>
  27.  
    <dependency>
  28.  
    <groupId>org.springframework.boot</groupId>
  29.  
    <artifactId>spring-boot-starter-validation</artifactId>
  30.  
    </dependency>
  31.  
    </dependencies>
  32.  
     
  33.  
    </project>

2.3.2      实体类ChatMessagesDto.java

  用于存放发送的消息信息,注解使用了lombok,如果没有使用lombok可以自动生成构造方法以及get和set方法

  1.  
    package com.framewiki.gpt.dto.response;
  2.  
     
  3.  
    import lombok.AllArgsConstructor;
  4.  
    import lombok.Data;
  5.  
    import lombok.NoArgsConstructor;
  6.  
     
  7.  
    /**
  8.  
    * @ProjectName: framewiki-gpt
  9.  
    * @Package: com.framewiki.gpt.dto
  10.  
    * @ClassName: ChatMessagesDto
  11.  
    * @Description: java类作用描述
  12.  
    * @Author: xiaLin
  13.  
    * @Date: 2023/6/10 22:30
  14.  
    * @Version: 1.0
  15.  
    */
  16.  
    @Data
  17.  
    @NoArgsConstructor
  18.  
    @AllArgsConstructor
  19.  
    public class ChatMessagesDto {
  20.  
     
  21.  
    /**
  22.  
    * 消息角色
  23.  
    * system
  24.  
    * user
  25.  
    * assistant
  26.  
    */
  27.  
    private String role;
  28.  
     
  29.  
    /**
  30.  
    * 消息内容
  31.  
    */
  32.  
    private String content;
  33.  
    }

2.3.3      实体类CreateChatCompletionDto.java

  1.  
    package com.framewiki.gpt.dto.request;
  2.  
     
  3.  
    import lombok.Data;
  4.  
     
  5.  
    /**
  6.  
    * @ProjectName: framewiki-gpt
  7.  
    * @Package: com.framewiki.gpt.dto.request
  8.  
    * @ClassName: CreateChatCompletionDto
  9.  
    * @Description: java类作用描述
  10.  
    * @Author: xiaLin
  11.  
    * @Date: 2023/6/21 22:07
  12.  
    * @Version: 1.0
  13.  
    */
  14.  
    @Data
  15.  
    public class CreateChatCompletionDto {
  16.  
     
  17.  
    /**
  18.  
    * 内容
  19.  
    */
  20.  
    private String content;
  21.  
     
  22.  
    /**
  23.  
    * 模型
  24.  
    */
  25.  
    private String model;
  26.  
     
  27.  
    /**
  28.  
    * 用户
  29.  
    */
  30.  
    private String user;
  31.  
    }

2.3.4      实体类ChatCompletionRequestDto.java

  用于发送的请求的参数实体类,参数释义如下:

  1.  
    package com.framewiki.gpt.dto.request;
  2.  
     
  3.  
    import com.alibaba.fastjson.annotation.JSONField;
  4.  
    import com.framewiki.gpt.dto.response.ChatMessagesDto;
  5.  
    import lombok.Builder;
  6.  
    import lombok.Data;
  7.  
     
  8.  
    import java.math.BigDecimal;
  9.  
    import java.util.List;
  10.  
     
  11.  
    /**
  12.  
    * @ProjectName: framewiki-gpt
  13.  
    * @Package: com.framewiki.gpt.dto
  14.  
    * @ClassName: ChatCompletionRequestDto
  15.  
    * @Description: 请求实体
  16.  
    * @Author: xiaLin
  17.  
    * @Date: 2023/6/10 22:27
  18.  
    * @Version: 1.0
  19.  
    */
  20.  
    @Data
  21.  
    @Builder
  22.  
    public class ChatCompletionRequestDto {
  23.  
     
  24.  
    /**
  25.  
    * 模型
  26.  
    * gpt-4
  27.  
    * gpt-4-0314
  28.  
    * gpt-4-32k
  29.  
    * gpt-4-32k-0314
  30.  
    * gpt-3.5-turbo
  31.  
    * gpt-3.5-turbo-0301
  32.  
    */
  33.  
    private String model;
  34.  
     
  35.  
    /**
  36.  
    * 温度,参数从0-2,越低表示越精准,越高表示越广发,回答的内容重复率越低
  37.  
    */
  38.  
    private BigDecimal temperature;
  39.  
     
  40.  
    /**
  41.  
    * 消息
  42.  
    */
  43.  
    private List<ChatMessagesDto> messages;
  44.  
     
  45.  
    /**
  46.  
    * 回复条数,一次对话回复的条数
  47.  
    */
  48.  
    private Integer n;
  49.  
     
  50.  
    /**
  51.  
    * 是否流式处理,就像ChatGPT一样的处理方式,会增量的发送信息。
  52.  
    */
  53.  
    private Boolean stream;
  54.  
     
  55.  
    /**
  56.  
    * 状态
  57.  
    */
  58.  
    private List<String> stop;
  59.  
     
  60.  
    /**
  61.  
    * 生成的答案允许的最大token数
  62.  
    */
  63.  
    @JSONField(name = "max_tokens")
  64.  
    private Integer maxTokens;
  65.  
     
  66.  
    /**
  67.  
    * 对话用户
  68.  
    */
  69.  
    private String user;
  70.  
    }

2.3.5      实体类ChatCompletionResponseDto.java

  用于接收请求返回的信息以及执行结果

  1.  
    package com.framewiki.gpt.dto.response;
  2.  
     
  3.  
    import lombok.Data;
  4.  
     
  5.  
    import java.util.List;
  6.  
     
  7.  
    /**
  8.  
    * @ProjectName: framewiki-gpt
  9.  
    * @Package: com.framewiki.gpt.dto
  10.  
    * @ClassName: ChatCompletionResponseDto
  11.  
    * @Description: 响应
  12.  
    * @Author: xiaLin
  13.  
    * @Date: 2023/6/16 23:18
  14.  
    * @Version: 1.0
  15.  
    */
  16.  
    @Data
  17.  
    public class ChatCompletionResponseDto {
  18.  
    /**
  19.  
    * ID
  20.  
    */
  21.  
    private String id;
  22.  
     
  23.  
    /**
  24.  
    * 返回的内容
  25.  
    */
  26.  
    private String object;
  27.  
     
  28.  
    /**
  29.  
    * 模型
  30.  
    */
  31.  
    private String model;
  32.  
     
  33.  
    /**
  34.  
    * 创建时间
  35.  
    */
  36.  
    private long created;
  37.  
     
  38.  
    /**
  39.  
    * 用户
  40.  
    */
  41.  
    private String user;
  42.  
     
  43.  
    /**
  44.  
    * 选择
  45.  
    */
  46.  
    private List<ChatCompletionChoiceDto> choices;
  47.  
     
  48.  
    /**
  49.  
    * 用量
  50.  
    */
  51.  
    private ChatCompletionUsageDto usage;
  52.  
    }

2.3.6      实体类ChatCompletionUsageDto.java

  1.  
    package com.framewiki.gpt.dto.response;
  2.  
     
  3.  
    import com.alibaba.fastjson.annotation.JSONField;
  4.  
    import lombok.Data;
  5.  
     
  6.  
    /**
  7.  
    * @ProjectName: framewiki-gpt
  8.  
    * @Package: com.framewiki.gpt.dto
  9.  
    * @ClassName: ChatCompletionUsageDto
  10.  
    * @Description: 用量信息
  11.  
    * @Author: xiaLin
  12.  
    * @Date: 2023/6/16 22:44
  13.  
    * @Version: 1.0
  14.  
    */
  15.  
    @Data
  16.  
    public class ChatCompletionUsageDto {
  17.  
     
  18.  
    /**
  19.  
    * 输入 token 数量
  20.  
    */
  21.  
    @JSONField(name = "prompt_tokens")
  22.  
    private int promptTokens;
  23.  
     
  24.  
    /**
  25.  
    * 完成 token数量
  26.  
    */
  27.  
    @JSONField(name = "completion_tokens")
  28.  
    private int completionTokens;
  29.  
     
  30.  
    /**
  31.  
    * token 总数
  32.  
    */
  33.  
    @JSONField(name = "total_tokens")
  34.  
    private int totalTokens;
  35.  
    }

2.3.7      实体类ChatCompletionChoiceDto.java

  用于接收ChatGPT返回的数据

  1.  
    package com.framewiki.gpt.dto.response;
  2.  
     
  3.  
    import lombok.Data;
  4.  
     
  5.  
    /**
  6.  
    * @ProjectName: framewiki-gpt
  7.  
    * @Package: com.framewiki.gpt.dto
  8.  
    * @ClassName: ChatCompletionChoiceDto
  9.  
    * @Description: java类作用描述
  10.  
    * @Author: xiaLin
  11.  
    * @Date: 2023/6/16 22:44
  12.  
    * @Version: 1.0
  13.  
    */
  14.  
    @Data
  15.  
    public class ChatCompletionChoiceDto {
  16.  
     
  17.  
    /**
  18.  
    * 搜索
  19.  
    */
  20.  
    private Integer index;
  21.  
     
  22.  
    /**
  23.  
    * 消息
  24.  
    */
  25.  
    private ChatMessagesDto message;
  26.  
     
  27.  
    /**
  28.  
    * 完成的原因
  29.  
    */
  30.  
    private String finishReason;
  31.  
    }

2.3.8      接口调用核心类ChatServiceImpl.java

  使用HttpURLConnection用于进行api接口的调用,支持post和get方法请求。

  url为配置文件open.ai.url的值,表示调用api的地址:https://api.openai.com/ ,token为获取的api-key。

  执行post或者get方法时增加头部信息headers.put("Authorization", "Bearer " + token); 用于通过接口鉴权。

  1.  
    package com.framewiki.gpt.service.impl;
  2.  
     
  3.  
    import com.cdkjframework.constant.EncodingConsts;
  4.  
    import com.cdkjframework.constant.IntegerConsts;
  5.  
    import com.cdkjframework.entity.http.HttpRequestEntity;
  6.  
    import com.cdkjframework.enums.HttpMethodEnums;
  7.  
    import com.cdkjframework.util.log.LogUtils;
  8.  
    import com.cdkjframework.util.network.http.HttpRequestUtils;
  9.  
    import com.cdkjframework.util.tool.StringUtils;
  10.  
    import com.framewiki.gpt.config.ChatConfig;
  11.  
    import com.framewiki.gpt.config.ChatConfiguration;
  12.  
    import com.framewiki.gpt.dto.request.ChatCompletionRequestDto;
  13.  
    import com.framewiki.gpt.dto.request.CreateChatCompletionDto;
  14.  
    import com.framewiki.gpt.dto.response.ChatCompletionResponseDto;
  15.  
    import com.framewiki.gpt.dto.response.ChatMessagesDto;
  16.  
    import com.framewiki.gpt.service.ChatService;
  17.  
    import lombok.RequiredArgsConstructor;
  18.  
    import org.springframework.stereotype.Service;
  19.  
     
  20.  
    import java.math.BigDecimal;
  21.  
    import java.util.ArrayList;
  22.  
    import java.util.HashMap;
  23.  
    import java.util.List;
  24.  
    import java.util.Map;
  25.  
     
  26.  
    /**
  27.  
    * @ProjectName: framewiki-gpt
  28.  
    * @Package: com.framewiki.gpt.service.impl
  29.  
    * @ClassName: ChatServiceImpl
  30.  
    * @Description: java类作用描述
  31.  
    * @Author: xiaLin
  32.  
    * @Date: 2023/6/16 22:58
  33.  
    * @Version: 1.0
  34.  
    */
  35.  
    @Service
  36.  
    @RequiredArgsConstructor
  37.  
    public class ChatServiceImpl implements ChatService {
  38.  
     
  39.  
    /**
  40.  
    * 日志
  41.  
    */
  42.  
    private LogUtils logUtils = LogUtils.getLogger(ChatServiceImpl.class);
  43.  
     
  44.  
    /**
  45.  
    * 配置信息
  46.  
    */
  47.  
    private final ChatConfiguration configuration;
  48.  
     
  49.  
    /**
  50.  
    * 地址
  51.  
    */
  52.  
    private final ChatConfig chatConfig;
  53.  
     
  54.  
    /**
  55.  
    * 创建对话
  56.  
    *
  57.  
    * @param content 消息内容
  58.  
    */
  59.  
    @Override
  60.  
    public ChatCompletionResponseDto createChatCompletion(CreateChatCompletionDto content) {
  61.  
    if (StringUtils.isNullAndSpaceOrEmpty(content.getModel())) {
  62.  
    content.setModel(model);
  63.  
    }
  64.  
    List<ChatMessagesDto> messages = new ArrayList<>();
  65.  
    ChatMessagesDto systemMessage = new ChatMessagesDto(role, content.getContent());
  66.  
    messages.add(systemMessage);
  67.  
    ChatCompletionRequestDto chatCompletionRequest = ChatCompletionRequestDto.builder()
  68.  
    .model(content.getModel())
  69.  
    .messages(messages)
  70.  
    .user(content.getUser())
  71.  
    .maxTokens(IntegerConsts.ONE_HUNDRED * IntegerConsts.FIVE)
  72.  
    .temperature(BigDecimal.ONE)
  73.  
    .build();
  74.  
     
  75.  
    HttpRequestEntity request = new HttpRequestEntity();
  76.  
    request.setRequestAddress(chatConfig.getCreateChatCompletion());
  77.  
    request.setMethod(HttpMethodEnums.POST);
  78.  
    request.setData(chatCompletionRequest);
  79.  
    request.setCharset(EncodingConsts.UTF8);
  80.  
    // 请求头
  81.  
    Map<String, String> headerMap = new HashMap<>(IntegerConsts.ONE);
  82.  
    headerMap.put(AUTHORIZATION, BEARER + configuration.getOpenaiApiKey());
  83.  
    request.setHeaderMap(headerMap);
  84.  
    ChatCompletionResponseDto response = null;
  85.  
    try {
  86.  
    response = HttpRequestUtils.httpRequest(request, ChatCompletionResponseDto.class);
  87.  
    response.setUser(content.getUser());
  88.  
    } catch (Exception e) {
  89.  
    logUtils.error(e);
  90.  
    }
  91.  
    // 返回结果
  92.  
    return response;
  93.  
    }
  94.  
    }

2.3.9      定义接口常量配置类ChatConfig.class

  用于维护支持的api接口列表

  1.  
    package com.framewiki.gpt.config;
  2.  
     
  3.  
    import org.springframework.beans.factory.annotation.Value;
  4.  
    import org.springframework.context.annotation.Configuration;
  5.  
    import org.springframework.stereotype.Component;
  6.  
     
  7.  
    /**
  8.  
    * @ProjectName: framewiki-gpt
  9.  
    * @Package: com.framewiki.gpt.config
  10.  
    * @ClassName: GptConfig
  11.  
    * @Description: GPT配置
  12.  
    * @Author: xiaLin
  13.  
    * @Date: 2023/6/10 18:16
  14.  
    * @Version: 1.0
  15.  
    */
  16.  
    @Component
  17.  
    @Configuration
  18.  
    public class ChatConfig {
  19.  
     
  20.  
    /**
  21.  
    * 环境
  22.  
    */
  23.  
    @Value("${spring.profiles.active}")
  24.  
    private String active;
  25.  
     
  26.  
    /**
  27.  
    * 默认环境
  28.  
    */
  29.  
    private final String defaultActive = "prod";
  30.  
     
  31.  
    /**
  32.  
    * 地址
  33.  
    */
  34.  
    private final String OPEN_AI_URI = "https://api.openai.com/v1/";
  35.  
     
  36.  
    /**
  37.  
    * 测试地址
  38.  
    */
  39.  
    private final String TEST_OPEN_AI_URI = "https://vpn.itizzy.com/v1/";
  40.  
     
  41.  
    /**
  42.  
    * 请求机构
  43.  
    * 列出模型
  44.  
    * 检索模型
  45.  
    */
  46.  
    private final String MODEL_LIST = "models";
  47.  
     
  48.  
    /**
  49.  
    * 聊天完成
  50.  
    */
  51.  
    private final String CREATE_CHAT_COMPLETION = "chat/completions";
  52.  
     
  53.  
    /**
  54.  
    * 创建对话
  55.  
    */
  56.  
    private final String CREATE_COMPLETION = "completions";
  57.  
     
  58.  
    /**
  59.  
    * ;
  60.  
    * 聊天完成地址
  61.  
    *
  62.  
    * @return
  63.  
    */
  64.  
    public String getCreateChatCompletion() {
  65.  
    StringBuffer address = new StringBuffer(getAddress());
  66.  
    address.append(CREATE_CHAT_COMPLETION);
  67.  
    // 返回结果
  68.  
    return address.toString();
  69.  
    }
  70.  
     
  71.  
    /**
  72.  
    * 获取地址
  73.  
    */
  74.  
    private String getAddress() {
  75.  
    if (active.startsWith(defaultActive)) {
  76.  
    return OPEN_AI_URI;
  77.  
    } else {
  78.  
    return TEST_OPEN_AI_URI;
  79.  
    }
  80.  
    }
  81.  
    }

2.3.10 接口调用OpenAi配置信息类ChatConfiguration.class

  1.  
    package com.framewiki.gpt.config;
  2.  
     
  3.  
    import lombok.Data;
  4.  
    import org.springframework.boot.context.properties.ConfigurationProperties;
  5.  
    import org.springframework.context.annotation.Configuration;
  6.  
     
  7.  
    /**
  8.  
    * @ProjectName: framewiki-gpt
  9.  
    * @Package: com.framewiki.gpt.config
  10.  
    * @ClassName: ChatConfiguration
  11.  
    * @Description: java类作用描述
  12.  
    * @Author: xiaLin
  13.  
    * @Date: 2023/6/10 19:35
  14.  
    * @Version: 1.0
  15.  
    */
  16.  
    @Data
  17.  
    @Configuration
  18.  
    @ConfigurationProperties(prefix = "open.ai.gpt")
  19.  
    public class ChatConfiguration {
  20.  
     
  21.  
    /**
  22.  
    * openai Api密钥
  23.  
    */
  24.  
    private String openaiApiKey;
  25.  
     
  26.  
    /**
  27.  
    * 机构ID
  28.  
    */
  29.  
    private String organizationId;
  30.  
     
  31.  
    /**
  32.  
    * appKey
  33.  
    */
  34.  
    private String appKey;
  35.  
    }

3     常见问题

3.1  OpenAi接口调用不通

因为https://api.openai.com/ 地址也被限制了,但是接口没有对地区做校验,因此可以自己搭建一个代理。

我采用的是亚马逊云代理的模式(新账号可申请1H1G、8G硬盘的云服务器),具体代理配置流程如下:

下载及安装nginx就不在此详说了。

部署nginx并修改/nginx/nginx.conf文件,配置接口代理路径如下

  1.  
    server {
  2.  
    listen 443 ssl;
  3.  
    server_name vpn.ai.com;
  4.  
     
  5.  
    ssl_certificate /usr/local/cert/vpn.ai.com.pem;
  6.  
    ssl_certificate_key /usr/local/cert/vpn.ai.com.key;
  7.  
     
  8.  
    ssl_session_cache shared:SSL:1m;
  9.  
    ssl_session_timeout 5m;
  10.  
     
  11.  
    ssl_ciphers HIGH:!aNULL:!MD5;
  12.  
    ssl_prefer_server_ciphers on;
  13.  
     
  14.  
    location / {
  15.  
    proxy_pass https://chat.openai.com/;
  16.  
    proxy_ssl_server_name on;
  17.  
    proxy_ssl_session_reuse off;
  18.  
    }
  19.  
    }
  20.  
    }

3.2  接口返回401

检查请求方法是否增加token字段以及key是否正确

4     总结

至此JAVA对OpenAI对接就已经完成了,并且也支持连续对话,大家可以在此基础上不断地完善和桥接到web服务,定制自己的ChatGPT助手了。我自己也搭建了个平台,不断地在完善中,想要体验的可以用微信登录体验。

 

项目开源地址:https://gitee.com/cdkjframework/chatgpt-server