开发喵星球

java实现GPT连续对话

java实现GPT连续对话

前言

早前面的文章中已经讲解了如何使用Java调用GPT API,今天来实现

核心代码实现分析

pom依赖

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>framewiki-gpt</artifactId>
        <groupId>com.framewiki.gpt</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>gpt-util</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.cdkjframework</groupId>
            <artifactId>cdkj-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.cdkjframework</groupId>
            <artifactId>cdkj-util</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
    </dependencies>

</project>

实体类ChatMessagesDto.java

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

package com.framewiki.gpt.dto.response;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessagesDto {

  /**
   * 消息角色
   * system
   * user
   * assistant
   */
  private String role;

  /**
   * 消息内容
   */
  private String content;
}

定义了一个私有的字符串类型变量 role,和content

实体类CreateChatCompletionDto.java

package com.framewiki.gpt.dto.request;

import lombok.Data;

@Data
public class CreateChatCompletionDto {

  /**
   * 内容
   */
  private String content;

  /**
   * 模型
   */
  private String model;

  /**
   * 用户
   */
  private String user;
}

定义了一个私有的字符串类型变量 content,和model,user

实体类ChatCompletionRequestDto.java

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

package com.framewiki.gpt.dto.request;

import com.alibaba.fastjson.annotation.JSONField;
import com.framewiki.gpt.dto.response.ChatMessagesDto;
import lombok.Builder;
import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

@Data
@Builder
public class ChatCompletionRequestDto {

  /**
   * 模型
   * gpt-4
   * gpt-4-0314
   * gpt-4-32k
   * gpt-4-32k-0314
   * gpt-3.5-turbo
   * gpt-3.5-turbo-0301
   */
  private String model;

  /**
   * 温度,参数从0-2,越低表示越精准,越高表示越广发,回答的内容重复率越低
   */
  private BigDecimal temperature;

  /**
   * 消息
   */
  private List<ChatMessagesDto> messages;

  /**
   * 回复条数,一次对话回复的条数
   */
  private Integer n;

  /**
   * 是否流式处理,就像ChatGPT一样的处理方式,会增量的发送信息。
   */
  private Boolean stream;

  /**
   * 状态
   */
  private List<String> stop;

  /**
   * 生成的答案允许的最大token数
   */
  @JSONField(name = "max_tokens")
  private Integer maxTokens;

  /**
   * 对话用户
   */
  private String user;
}

定义了一些api的参数,这些可以在官网查询到

实体类ChatCompletionResponseDto.java

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

package com.framewiki.gpt.dto.response;

import lombok.Data;

import java.util.List;

@Data
public class ChatCompletionResponseDto {
  /**
   * ID
   */
  private String id;

  /**
   * 返回的内容
   */
  private String object;

  /**
   * 模型
   */
  private String model;

  /**
   * 创建时间
   */
  private long created;

  /**
   * 用户
   */
  private String user;

  /**
   * 选择
   */
  private List<ChatCompletionChoiceDto> choices;

  /**
   * 用量
   */
  private ChatCompletionUsageDto usage;
}

实体类ChatCompletionUsageDto.java

package com.framewiki.gpt.dto.response;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

@Data
public class ChatCompletionUsageDto {

  /**
   * 输入 token 数量
   */
  @JSONField(name = "prompt_tokens")
  private int promptTokens;

  /**
   * 完成 token数量
   */
  @JSONField(name = "completion_tokens")
  private int completionTokens;

  /**
   * token 总数
   */
  @JSONField(name = "total_tokens")
  private int totalTokens;
}

实体类ChatCompletionChoiceDto.java

  用于接收ChatGPT返回的数据

package com.framewiki.gpt.dto.response;

import lombok.Data;

@Data
public class ChatCompletionChoiceDto {

  /**
   * 搜索
   */
  private Integer index;

  /**
   * 消息
   */
  private ChatMessagesDto message;

  /**
   * 完成的原因
   */
  private String finishReason;
}

接口调用核心类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); 用于通过接口鉴权。

package com.framewiki.gpt.service.impl;

import com.cdkjframework.constant.EncodingConsts;
import com.cdkjframework.constant.IntegerConsts;
import com.cdkjframework.entity.http.HttpRequestEntity;
import com.cdkjframework.enums.HttpMethodEnums;
import com.cdkjframework.util.log.LogUtils;
import com.cdkjframework.util.network.http.HttpRequestUtils;
import com.cdkjframework.util.tool.StringUtils;
import com.framewiki.gpt.config.ChatConfig;
import com.framewiki.gpt.config.ChatConfiguration;
import com.framewiki.gpt.dto.request.ChatCompletionRequestDto;
import com.framewiki.gpt.dto.request.CreateChatCompletionDto;
import com.framewiki.gpt.dto.response.ChatCompletionResponseDto;
import com.framewiki.gpt.dto.response.ChatMessagesDto;
import com.framewiki.gpt.service.ChatService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class ChatServiceImpl implements ChatService {

  /**
   * 日志
   */
  private LogUtils logUtils = LogUtils.getLogger(ChatServiceImpl.class);

  /**
   * 配置信息
   */
  private final ChatConfiguration configuration;

  /**
   * 地址
   */
  private final ChatConfig chatConfig;

  /**
   * 创建对话
   *
   * @param content 消息内容
   */
  @Override
  public ChatCompletionResponseDto createChatCompletion(CreateChatCompletionDto content) {
    if (StringUtils.isNullAndSpaceOrEmpty(content.getModel())) {
      content.setModel(model);
    }
    List<ChatMessagesDto> messages = new ArrayList<>();
    ChatMessagesDto systemMessage = new ChatMessagesDto(role, content.getContent());
    messages.add(systemMessage);
    ChatCompletionRequestDto chatCompletionRequest = ChatCompletionRequestDto.builder()
        .model(content.getModel())
        .messages(messages)
        .user(content.getUser())
        .maxTokens(IntegerConsts.ONE_HUNDRED * IntegerConsts.FIVE)
        .temperature(BigDecimal.ONE)
        .build();

    HttpRequestEntity request = new HttpRequestEntity();
    request.setRequestAddress(chatConfig.getCreateChatCompletion());
    request.setMethod(HttpMethodEnums.POST);
    request.setData(chatCompletionRequest);
    request.setCharset(EncodingConsts.UTF8);
    // 请求头
    Map<String, String> headerMap = new HashMap<>(IntegerConsts.ONE);
    headerMap.put(AUTHORIZATION, BEARER + configuration.getOpenaiApiKey());
    request.setHeaderMap(headerMap);
    ChatCompletionResponseDto response = null;
    try {
      response = HttpRequestUtils.httpRequest(request, ChatCompletionResponseDto.class);
      response.setUser(content.getUser());
    } catch (Exception e) {
      logUtils.error(e);
    }
    // 返回结果
    return response;
  }
}

代码的主要功能的解析:

  1. 导入了一系列的依赖包,其中包括了各种工具类和配置信息类。
  2. 定义了一个名为 ChatServiceImpl 的服务实现类,实现了 ChatService 接口。
  3. 使用了 Lombok 提供的 @RequiredArgsConstructor 注解,生成一个带有所有必需字段的构造函数,这些字段包括 ChatConfigurationChatConfig
  4. 在类中声明了日志工具类 LogUtils 和一些配置信息。
  5. 实现了 createChatCompletion 方法,用于创建对话的完成。这个方法接收一个 CreateChatCompletionDto 参数,其中包含了对话的相关信息,例如对话模型、用户和消息内容。
  6. 在方法中根据参数构建了对话请求的对象 ChatCompletionRequestDto,设置了模型、消息列表、用户、最大生成 token 数量和温度。
  7. 构建了一个 HttpRequestEntity 对象,设置了请求地址、请求方法、请求数据、字符编码和请求头信息(包括授权信息)。
  8. 调用 HttpRequestUtils 中的方法发送 HTTP 请求,并将返回结果映射为 ChatCompletionResponseDto 对象。
  9. 捕获了可能的异常,并在日志中记录错误信息。
  10. 最后返回了对话的响应结果。

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

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

package com.framewiki.gpt.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

@Component
@Configuration
public class ChatConfig {

  /**
   * 环境
   */
  @Value("${spring.profiles.active}")
  private String active;

  /**
   * 默认环境
   */
  private final String defaultActive = "prod";

  /**
   * 地址
   */
  private final String OPEN_AI_URI = "https://api.openai.com/v1/";

  /**
   * 测试地址
   */
  private final String TEST_OPEN_AI_URI = "https://vpn.itizzy.com/v1/";

  /**
   * 请求机构
   * 列出模型
   * 检索模型
   */
  private final String MODEL_LIST = "models";

  /**
   * 聊天完成
   */
  private final String CREATE_CHAT_COMPLETION = "chat/completions";

  /**
   * 创建对话
   */
  private final String CREATE_COMPLETION = "completions";

  /**
   * ;
   * 聊天完成地址
   *
   * @return
   */
  public String getCreateChatCompletion() {
    StringBuffer address = new StringBuffer(getAddress());
    address.append(CREATE_CHAT_COMPLETION);
    // 返回结果
    return address.toString();
  }

  /**
   * 获取地址
   */
  private String getAddress() {
    if (active.startsWith(defaultActive)) {
      return OPEN_AI_URI;
    } else {
      return TEST_OPEN_AI_URI;
    }
  }
}

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

package com.framewiki.gpt.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "open.ai.gpt")
public class ChatConfiguration {

  /**
   * openai Api密钥
   */
  private String openaiApiKey;

  /**
   * 机构ID
   */
  private String organizationId;

  /**
   * appKey
   */
  private String appKey;
}

常见问题

OpenAi接口调用不通

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

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

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

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

    server {
        listen       443 ssl;
        server_name  vpn.ai.com;

        ssl_certificate      /usr/local/cert/vpn.ai.com.pem;
        ssl_certificate_key  /usr/local/cert/vpn.ai.com.key;

       ssl_session_cache    shared:SSL:1m;
       ssl_session_timeout  5m;

       ssl_ciphers  HIGH:!aNULL:!MD5;
       ssl_prefer_server_ciphers  on;
        
       location / {
           proxy_pass https://chat.openai.com/;
           proxy_ssl_server_name on;
           proxy_ssl_session_reuse off;
       }
    }
}

接口返回401

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

总结

至此Java连接GPT 就已经完成了,并且也支持连续对话,大家可以在此基础上不断地完善和桥接到web服务,定制自己的ChatGPT助手了。

   
分类:玩技术 作者:荡荡, 浩浩 发表于:2024-03-21 13:24:50 阅读量:149
<<   >>


powered by kaifamiao