【开源免费】ChatGPT-Java版SDK重磅更新收获2.3k,支持插件模式、实现ChatGpt联网操作。

everybody 七夕来了还单着么?

一、简介

ChatGPT Java版SDK开源地址:https://github.com/Grt1228/chatgpt-java,目前收获将近2200+个star🌟。

最新版:1.1.1-beta0

xml 复制代码
<dependency>
    <groupId>com.unfbx</groupId>
    <artifactId>chatgpt-java</artifactId>
    <version>1.1.1-beta0</version>
</dependency>

二、特性支持

  • 支持GPT插件模式 参考实现 PluginTest
  • 支持当key异常(失效、过期、封禁)时,自定义动态处理key 参考实现DynamicKeyOpenAiAuthInterceptor
  • 支持当key异常时的告警处理(钉钉、飞书、email、企业微信等等需要自定义开发)参考实现DynamicKeyOpenAiAuthInterceptor
  • 支持多种Tokens计算方式
  • 支持自定义OkhttpClient
  • 支持自定义多Apikey
  • 支持自定义ApiKey的获取策略
  • 支持余额查询
  • 支持个人账户信息查询
  • 支持GPT3、GPT3.5、GPT4.0、GPT3.5---0614、GPT4.0---0614...
  • 支持全部OpenAI的Api

三、插件机制

本文重点介绍插件机制,原有功能使用可以参考官方文档

https://chatgpt-java.unfbx.com/

3.1、插件原理

ChatGpt在早些时候推出function call功能,这个在我看来其实就是插件的本质,通过自定义function实现插件的功能,SDK在1.0.14+版本的时候已经支持了原生的function call调用。但是function call调用逻辑比较复杂难懂,很多小伙伴反应不太会使用。于是我基于function call做了下定制封装实现Plugin功能。(可能存在不合理的地方欢迎指正)

3.2、使用步骤

想看示例的朋友可以直接看:PluginTest

3.2.1、简介------插件抽象类

插件抽象类,定义了插件的必须参数:插件名称,方法,描述,参数,必须参数,插件的请求值R,插件的返回值T

需要重点关注R和T两个泛型。

插件抽象类还包含两个重要的抽象方法: func和content方法。这两个方法需要自己实现。

方法 功能
public abstract T func(R args); 接受一个插件参数,返回插件返回值。后面有示例演示。
public abstract String content(T t); 构建给gpt的参数信息
java 复制代码
@Data
@AllArgsConstructor
public abstract class PluginAbstract<R extends PluginParam, T> {

    private Class<?> R;
    private String name;
    private String function;
    private String description;
    private List<Arg> args;
    private List<String> required;
    private Parameters parameters;
    public PluginAbstract(Class<?> r) {
        R = r;
    }

    public void setRequired(List<String> required) {
        if (CollectionUtil.isEmpty(required)) {
            this.required = this.getArgs().stream().filter(e -> e.isRequired()).map(Arg::getName).collect(Collectors.toList());
            return;
        }
        this.required = required;
    }

    private void setRequired() {
        if (CollectionUtil.isEmpty(required)) {
            this.required = this.getArgs().stream().filter(e -> e.isRequired()).map(Arg::getName).collect(Collectors.toList());
        }
    }

    private void setParameters() {
        JSONObject properties = new JSONObject();
        args.forEach(e -> {
            JSONObject param = new JSONObject();
            param.putOpt("type", e.getType());
            param.putOpt("enum", e.getEnumDictValue());
            param.putOpt("description", e.getDescription());
            properties.putOpt(e.getName(), param);
        });
        this.parameters = Parameters.builder()
                .type("object")
                .properties(properties)
                .required(this.getRequired())
                .build();
    }

    public void setArgs(List<Arg> args) {
        this.args = args;
        setRequired();
        setParameters();
    }

    @Data
    public static class Arg {
        private String name;
        private String type;
        private String description;
        @JsonIgnore
        private boolean enumDict;
        @JsonProperty("enum")
        private List<String> enumDictValue;
        @JsonIgnore
        private boolean required;
    }

    public abstract T func(R args);

    public abstract String content(T t);
}

3.2.2、创建插件

创建自定义插件继承PluginAbstract抽象类。

WeatherReq,WeatherResp在这省略 。完整测试源码请看:https://github.com/Grt1228/chatgpt-java test包目录。

举例实现天气查询插件。

java 复制代码
public class WeatherPlugin extends PluginAbstract<WeatherReq, WeatherResp> {
    public WeatherPlugin(Class<?> r) {
        super(r);
    }

    @Override
    public WeatherResp func(WeatherReq args) {
        //模拟天气查询,真实使用场景需要调用第三方接口查询真实天气
        WeatherResp weatherResp = new WeatherResp();
        weatherResp.setTemp("25到28摄氏度");
        weatherResp.setLevel(3);
        return weatherResp;
    }
    @Override
    public String content(WeatherResp weatherResp) {
    //构建chatgpt需要的content参数
        return "当前天气温度:" + weatherResp.getTemp() + ",风力等级:" + weatherResp.getLevel();
    }
}

3.2.3、使用插件

插件使用同样支持阻塞输出和流式输出两种方式,可以自己根据实际场景使用。

创建OpenAi客户端

客户端的创建和原来保持一致

java 复制代码
/**
 * 描述: 插件测试类
 *
 * @author https:www.unfbx.com
 * 2023-08-18
 */
@Slf4j
public class PluginTest {

    private OpenAiClient openAiClient;
    private OpenAiStreamClient openAiStreamClient;

    @Before
    public void before() {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
        //!!!!千万别再生产或者测试环境打开BODY级别日志!!!!
        //!!!生产或者测试环境建议设置为这三种级别:NONE,BASIC,HEADERS,!!!
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
        OkHttpClient okHttpClient = new OkHttpClient
                .Builder()
                .addInterceptor(httpLoggingInterceptor)
                .addInterceptor(new OpenAiResponseInterceptor())
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();
        openAiClient = OpenAiClient.builder()
                .okHttpClient(okHttpClient)
                .apiKey(Arrays.asList("sk-********************************"))
                .apiHost("https://dgr.life/")
                .build();
        openAiStreamClient = OpenAiStreamClient.builder()
                //支持多key传入,请求时候随机选择
                .apiKey(Arrays.asList("sk-********************************"))
                .apiHost("https://dgr.life/")
                .build();
    }
}

流式输出

java 复制代码
@Test
    public void streamPlugin() {
        WeatherPlugin plugin = new WeatherPlugin(WeatherReq.class);
        plugin.setName("知心天气");
        plugin.setFunction("getLocationWeather");
        plugin.setDescription("提供一个地址,方法将会获取该地址的天气的实时温度信息。");
        PluginAbstract.Arg arg = new PluginAbstract.Arg();
        arg.setName("location");
        arg.setDescription("地名");
        arg.setType("string");
        arg.setRequired(true);
        plugin.setArgs(Collections.singletonList(arg));

//        Message message1 = Message.builder().role(Message.Role.USER).content("秦始皇统一了哪六国。").build();
        Message message2 = Message.builder().role(Message.Role.USER).content("获取上海市的天气现在多少度,然后再给出3个推荐的户外运动。").build();
        List<Message> messages = new ArrayList<>();
//        messages.add(message1);
        messages.add(message2);
        //默认模型:GPT_3_5_TURBO_16K_0613
        //有四个重载方法,都可以使用
        openAiStreamClient.streamChatCompletionWithPlugin(messages, new ConsoleEventSourceListener(), plugin);
        CountDownLatch countDownLatch = new CountDownLatch(1);
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

阻塞输出

java 复制代码
    @Test
    public void plugin() {
        WeatherPlugin plugin = new WeatherPlugin(WeatherReq.class);
        plugin.setName("知心天气");
        plugin.setFunction("getLocationWeather");
        plugin.setDescription("提供一个地址,方法将会获取该地址的天气的实时温度信息。");
        PluginAbstract.Arg arg = new PluginAbstract.Arg();
        arg.setName("location");
        arg.setDescription("地名");
        arg.setType("string");
        arg.setRequired(true);
        plugin.setArgs(Collections.singletonList(arg));

//        Message message1 = Message.builder().role(Message.Role.USER).content("秦始皇统一了哪六国。").build();
        Message message2 = Message.builder().role(Message.Role.USER).content("获取上海市的天气现在多少度,然后再给出3个推荐的户外运动。").build();
        List<Message> messages = new ArrayList<>();
//        messages.add(message1);
        messages.add(message2);
        //默认模型:GPT_3_5_TURBO_16K_0613
        //有四个重载方法,都可以使用
        ChatCompletionResponse response = openAiClient.chatCompletionWithPlugin(messages, plugin);
        log.info("自定义的方法返回值:{}", response.getChoices().get(0).getMessage().getContent());
    }

四、完结

上面已经完整介绍了整个插件的使用过程,方案不一定是最合理的,也是beat版本,欢迎交流。

如果觉的文章对你有帮助帮忙点个赞。

完整测试源码请看https://github.com/Grt1228/chatgpt-java

记得帮忙点个star 🌟🌟🌟哦

首发微信号:程序员的黑洞

相关推荐
江喜原2 分钟前
微服务下设计一个注解标识是否需要登录
java·微服务·架构·登录
ABin-阿斌9 分钟前
SpringBoot 整合 Easy_Trans 实现翻译的具体介绍
java·spring boot·后端
菜鸟求带飞_11 分钟前
算法打卡:第十一章 图论part03
java·数据结构·算法·深度优先·图论
圆头圆脑圆JAVA11 分钟前
简单了解微服务--黑马(在更)
java·spring boot·微服务
木子欢儿19 分钟前
在 Debian 12 上安装 Java 21
java·运维·开发语言·debian
一二小选手22 分钟前
【高级编程】XML DOM4J解析XML文件(含案例)
xml·java
终末圆23 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
就这个java爽!23 分钟前
超详细的XML介绍【附带dom4j操作XML】
xml·java·开发语言·数据库·青少年编程·eclipse
kunkun10125 分钟前
Mybatis的XML实现方法
xml·java·mybatis
libai32 分钟前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32