Java调用ChatGPT(基于SpringBoot和Vue)实现连续对话、流式输出和自定义baseUrl

news2024/11/18 3:34:39

目录

  • 版本更新说明
  • 1. 配置阶段
    • 1.1 依赖引入
    • 1.2 配置application.yml文件
    • 1.3 注解添加
  • 2. 使用
    • 2.1 生成回答
      • 2.1.1 测试
    • 2.2 生成图片
      • 2.2.1 测试
    • 2.3 下载图片
      • 2.3.1 测试
    • 2.4 生成流式回答
      • 2.4.1 流式回答输出到IDEA控制台
      • 2.4.2 流式回答输出到浏览器页面
      • 2.4.3 流式回答结合Vue输出到前端界面
    • 2.5 查询账单和订阅
      • 2.5.1 测试
    • 2.6 自定义baseUrl
    • 2.7 模型(model)
    • 2.8 编辑(edit)
    • 2.9 向量(embeddings)
    • 2.10 转录(transcription)
    • 2.11 翻译(translation)
  • 3. 扩展
    • 3.1 自定义OpenAiProxyService
  • 4 自定义OpenAi服务端
    • 4.1 介绍
    • 4.2 测试
      • 4.2.1 基于SpringBoot使用OpenAiUtils
      • 4.2.2 基于Main方法使用OpenAiProxyService
  • 5 AI助手展示

源码及更详细的介绍说明参见Git上的README.md文档 https://github.com/asleepyfish/chatgpt

本文Demo(SpringBoot和Main方法Demo均包括)的Git地址:https://github.com/asleepyfish/chatgpt-demo

流式输出结合Vue前端的Demo的Git地址:https://github.com/asleepyfish/chatgpt-vue

后续使用方法和api版本更新均在Github的README.md文档更新,本博客暂停更新~

2023-05-28更新

制作了一个基于本文sdk包的对话网站,可前往http://chatgpt.alpacos.cn/体验。
[注]: 由于本人的服务器的带宽较低,首次加载页面可能会略有些慢,请耐心等待,详细使用说明请查看另一篇博客~ 👇
ChatGPT网页版(基于SpringBoot和Vue)

2023-07-17更新

所有的测试Demo均包括结合Spring框架的(ChatGPTController类中)和main方法使用(MainTest类中)Demo地址:https://github.com/asleepyfish/chatgpt-demo

版本更新说明

  • 1.1.5 增加查询账单功能billingUsage(单位:美元),可以选择传入开始和结束日期查询(最多100天),或者不传入参,此时表示查询所有日期账单。
  • 1.1.6 增加自定义OpenAiProxyService功能,支持单个SpringBoot中添加多个OpenAiProxyService实例,每个实例可以拥有个性化的参数;查询账单功能优化。
  • 1.2.0 增加subscription方法(查询订阅信息,包括订阅到期日和账号额度等信息,但是没有使用量情况,使用通过billingUsage方法查询使用量),增加billing方法,整合了subscriptionbillingUsage方法,出参包括订阅到期日、额度、使用量、余量等信息。增加对内部cache的多种操作,包括获取,赋值等操作。
  • 1.2.1 billing方法中出参dueDate取值逻辑修改,ChatGPTProperties类支持build链式创建对象。
  • 1.3.0 新增以下方法,每种方法均包含多种重载方法,具体使用请参考:https://github.com/asleepyfish/chatgpt-demo
    • listModelsgetModel
    • edit
    • embeddings
    • transcriptiontranslation
    • createImageEditcreateImageVariation
    • listFilesuploadFiledeleteFileretrieveFileretrieveFileContent
    • createFineTunecreateFineTuneCompletionlistFineTunesretrieveFineTunecancelFineTunelistFineTuneEventsdeleteFineTune
    • createModeration
  • 1.3.1 支持自定义baseUrl,默认为 https://api.openai.com/ ,配置参数在ChatGPTProperties类中,可通过application.yml配置。
  • 1.3.2 修复自定义baseUrl后无法访问bug,自定义baseUrl必须以/结尾。新增自定义属于自己的baseUrl示例。

1. 配置阶段

1.1 依赖引入

pom.xml中引入依赖(当前最新版本为1.3.2,可前往Github页面查看当前最新版本)

        <dependency>
            <groupId>io.github.asleepyfish</groupId>
            <artifactId>chatgpt</artifactId>
            <version>1.3.2</version>
        </dependency>

1.2 配置application.yml文件

application.yml文件中配置相关参数(Optional为可选参数)

参数解释
token申请的API KEYS
proxy-host (Optional)代理的ip
proxy-port (Optional)代理的端口
model (Optional)model可填可不填,默认即text-davinci-003
chat-model (Optional)可填可不填,默认即gpt-3.5-turbo (ChatGPT当前最强模型,生成回答使用的就是这个模型)
retries (Optional)指的是当chatgpt第一次请求回答失败时,重新请求的次数(增加该参数的原因是因为大量访问的原因,在某一个时刻,chatgpt服务将处于无法访问的情况,不填的默认值为5)
session-expiration-time (Optional)(单位(min))为这个会话在多久不访问后被销毁,这个值不填的时候,即表示所有问答处于同一个会话之下,相同user的会话永不销毁(增加请求消耗)
base-url (Optional)默认为 https://api.openai.com/ ,可不填

例:

chatgpt:
  token: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #必填
#  proxy-host: 127.0.0.1 #需要代理时必填
#  proxy-port: 7890 #需要代理时必填
#  model: text-davinci-003 #可选
#  chat-model: gpt-3.5-turbo #可选
#  retries: 10 #可选,默认为5
#  session-expiration-time: 30 #可选,不填则会话永不过期
#  base-url: https://apps.ichati.cn/1d6f32f8-b59d-46f8-85e9-7d434bxxxxxx/ #可选,默认为https://api.openai.com/,请记住务必以/结尾

其中token、proxy-host、proxy-port是必填的

上面的session-expiration-time参数很重要,是用来表示这个会话在多久不访问后被销毁,从而实现联系上下文的连续对话。

实现方式是通过ChatCompletionRequest中的user来区分某个会话,而session-expiration-time表示这个会话在多久不访问后被销毁。

如果这里看不懂请看2.1节示例

1.3 注解添加

启动类上加入图中的注解则将服务注入到Spring中。
在这里插入图片描述

2. 使用

2.1 生成回答

提供了工具类OpenAiUtils,里面提供了相关方法进行调用。
其中最简单的使用方法是:

OpenAiUtils.createChatCompletion(content);// 不建议使用

入参content即输入的问题的字符串。但是不建议使用。

这里建议使用下面的方式,通过传入user的值,再结合session-expiration-time参数,可以实现指定某次会话,或者某个用户的连续对话。

OpenAiUtils.createChatCompletion(content, user);// 建议使用

还提供一个通用的静态方法是

public static List<String> createChatCompletion(ChatCompletionRequest chatCompletionRequest) {...}

入参ChatCompletionRequest 里包含模型的一些可调参数。

OpenAiUtils类中还提供了多个可供选择的静态方法,可以自行查看。

上述方法的返回参数是一个list,是因为调整参数返回答案n可以一次性返回多条不同的解答(nChatCompletionRequest类中一个参数)。

2.1.1 测试

测试代码:

@PostMapping("/chatTest")
public List<String> chatTest(String content) {
    return OpenAiUtils.createChatCompletion(content, "testUser");
}

Post请求

入参输入:Java序列化的方式

返回结果:

[
  "\n\nJava序列化是将Java对象转换为字节序列的过程,以便在网络上传输或将其保存到磁盘上。Java提供了两种序列化方式:\n\n1. 基于Serializable接口的序列化\n\nSerializable接口是Java提供的一个标记接口,用于标记一个类可以被序列化。如果一个类实现了Serializable接口,那么它的所有非瞬态字段都会被序列化。序列化的过程可以通过ObjectOutputStream类来实现,反序列化的过程可以通过ObjectInputStream类来实现。\n\n2. 基于Externalizable接口的序列化\n\nExternalizable接口也是Java提供的一个标记接口,用于标记一个类可以被序列化。与Serializable接口不同的是,Externalizable接口需要实现writeExternal和readExternal方法,这两个方法分别用于序列化和反序列化。在序列化的过程中,只有被writeExternal方法显式写入的字段才会被序列化,而在反序列化的过程中,只有被readExternal方法显式读取的字段才会被反序列化。\n\n总的来说,基于Serializable接口的序列化更加简单,但是它会序列化所有非瞬态字段,包括一些不需要序列化的字段,而基于Externalizable接口的序列化可以更加灵活地控制序列化的过程。"
]

再次输入:有没有更加高效的序列化框架

返回结果:

[
  "是的,Java中有很多高效的序列化框架,以下是一些常用的序列化框架:\n\n1. Protobuf\n\nProtobuf是Google开发的一种高效的序列化框架,它可以将结构化数据序列化为二进制格式,支持多种编程语言。相比于Java自带的序列化方式,Protobuf序列化后的数据更小,解析速度更快。\n\n2. Kryo\n\nKryo是一个快速、高效的Java序列化框架,它可以将Java对象序列化为字节数组,支持多种数据类型。Kryo序列化的速度比Java自带的序列化方式快很多,序列化后的数据也更小。\n\n3. FST\n\nFST是一个高性能的Java序列化框架,它可以将Java对象序列化为字节数组,支持多种数据类型。FST序列化的速度比Java自带的序列化方式快很多,序列化后的数据也更小。\n\n4. Avro\n\nAvro是一个高效的数据序列化系统,它可以将结构化数据序列化为二进制格式,支持多种编程语言。Avro序列化后的数据比Java自带的序列化方式更小,解析速度也更快。\n\n总的来说,这些高效的序列化框架都比Java自带的序列化方式更快、更小、更灵活,可以根据具体的需求选择合适的框架。"
]

可以看出上述两次问答是在一次会话中的,而前面所说的参数session-expiration-time即这个user所代表的会话多久没被继续访问时的销毁时间。单位(min)

2.2 生成图片

最简单的使用方式是

OpenAiUtils.createImage(prompt);

入参表示生成图片的描述文字,还提供了一个通用的静态方法

public static List<String> createImage(CreateImageRequest createImageRequest) {...}

入参CreateImageRequest中有一些可以使用的参数,其中n表示生成图片的数量,responseFormat表示生成图片的格式,格式中分为urlb64_json两种,如果希望返回的是url,则返回的url会在生成一个小时后消失,默认值是url

2.2.1 测试

测试代码

    @Test
    public void testGenerateImg() {
        OpenAiUtils.createImage("英短").forEach(System.out::println);
    }

结果
默认情况下会生成一个url,点击去就可以看到图片。
在这里插入图片描述

2.3 下载图片

在3.2的基础上做了优化,直接使用responseFormatb64_json然后解析成图片返回。简单使用方式如下:

OpenAiUtils.downloadImage(prompt, response);

通用方式如下:

public static void downloadImage(CreateImageRequest createImageRequest, HttpServletResponse response) {...}

CreateImageRequest对象中设置的返回参数n大于1时,会将图片打包成一个zip包返回,当n等于1时直接返回图片。

2.3.1 测试

测试代码

@RestController
public class ChatGPTController {
    @GetMapping("/downloadImage")
    public void downloadImage(String prompt, HttpServletResponse response) {
        OpenAiUtils.downloadImage(prompt, response);
    }
}

发送get请求,然后选择Send and Download
在这里插入图片描述

我用的get 工具是idea里面下载的插件Fast Request的,用Postman也是可以的,但是要选择 Send and Download,上图中绿色的箭头是Send,蓝色的是Send and Download。

在这里插入图片描述

2.4 生成流式回答

生成流式回答的方法是OpenAiUtilscreateStreamChatCompletion方法,本工具类重载了同名的多个参数的方法,其中最通用的方法是

public static void createStreamChatCompletion(ChatCompletionRequest chatCompletionRequest, OutputStream os) {...}

最简单的方法是

public static void createStreamChatCompletion(String content) {...}

其中的content即本次对话的问题。

这里需要主义的是,上述第一个方法中的OutputStream os其实是一个必传的对象,上述的最简单的方法实际上是默认传递的System.out这个os对象,也就是将流式问答的结果显示到IDEA的控制台。

如果需要将流式问答的结果显示到其他界面可以自发的传入OutputStream os对象,这里有一个简便的方法是

public static void createStreamChatCompletion(String content, OutputStream os) {...}

只需要输入问题,和输出流对象即可。

下面将举例具体说明。(本文所有Demo的示例地址: https://github.com/asleepyfish/chatgpt-demo)

2.4.1 流式回答输出到IDEA控制台

代码如下:

@GetMapping("/streamChat")
public void streamChat(String content) {
    // OpenAiUtils.createStreamChatCompletion(content, System.out);
    // 下面的默认和上面这句代码一样,是输出结果到控制台
    OpenAiUtils.createStreamChatCompletion(content);
}

然后使用Postman或者其他可以发送Get请求的工具发送请求。

本次测试的结果如下面的Gif图所示

在这里插入图片描述

2.4.2 流式回答输出到浏览器页面

上述的方法中输出流传入的是System.out对象,该对象实际上就是一个PrintStream对象,会把输出结果展示到控制台。

如果需要将输出结果在浏览器展示,可以从前端传入一个HttpServletResponse response对象,拿到这个response以后将response.getOutputStream()这个输出流对象传入createStreamChatCompletion方法的入参中。同时,为了避免结果输出到浏览器产生乱码和支持流式输出,需要ContentTypeCharacterEncoding

具体代码如下:

@GetMapping("/streamChatWithWeb")
public void streamChatWithWeb(String content, HttpServletResponse response) throws IOException {
    // 需要指定response的ContentType为流式输出,且字符编码为UTF-8
    response.setContentType("text/event-stream");
    response.setCharacterEncoding("UTF-8");
    // 禁用缓存
    response.setHeader("Cache-Control", "no-cache");    
    OpenAiUtils.createStreamChatCompletion(content, response.getOutputStream());
}

测试结果过程的Gif图如下所示:

在这里插入图片描述

2.4.3 流式回答结合Vue输出到前端界面

调用的后端方法同2.4.2节方法streamChatWithWeb,前端只需要在界面传入问题,点击提问按钮即可返回结果流式输出到文本框中。

测试结果过程的Gif图如下所示:

在这里插入图片描述
Vue3 Demo的Git地址在文章开头有~

2.5 查询账单和订阅

查询账单提供了两个方法,金额单位均为美元(USD),且均未对小数位截取,可以根据需要自行选择保留结果小数点位数。

第一个是可以传入开始和结束日期,按照指定日期区间查询的方法:

public String billingUsage(String startDate, String endDate) {...}

其中startDateendDate区间范围不超过100天。

第二个方法是一个入参为可变参数的方法,当不传入参时,查询从2023年1月1日距今的账单的方法,如果有人的订阅日早于2023年1月1日可以传入自定义账单起始日期:

public String billingUsage(String... startDate) {...}

查询订阅提供了一个方法,这个方法的出参中包括了订阅到期日,总额度等信息:

public Subscription subscription() {...}

由于查询总额度和查询使用量是两个接口,这里封装了一个方法来将几个比较有用的参数统一返回的方法,方法如下:

public Billing billing(String... startDate) {...}

这个方法的入参也是一个可变入参,不传参时,startDate默认为2023-01-01,如果账单开始日早于该天,可以传入指定的startDate。出参Billing中有四个参数:dueDate(额度到期日),total(额度总量),usage(已使用量),balance(余额)。

2.5.1 测试

测试代码如下:

@GetMapping("/billing")
public void billing() {
    String monthUsage = OpenAiUtils.billingUsage("2023-04-01", "2023-05-01");
    log.info("四月使用:{}美元", monthUsage);
    String totalUsage = OpenAiUtils.billingUsage();
    log.info("一共使用:{}美元", totalUsage);
    String stageUsage = OpenAiUtils.billingUsage("2023-01-31");
    log.info("自从2023/01/31使用:{}美元", stageUsage);
    Subscription subscription = OpenAiUtils.subscription();
    log.info("订阅信息(包含到期日期,账户总额度等信息):{}", subscription);
    // dueDate为到期日,total为总额度,usage为使用量,balance为余额
    Billing totalBilling = OpenAiUtils.billing();
    log.info("历史账单信息:{}", totalBilling);
    // 默认不传参的billing方法的使用量usage从2023-01-01开始,如果用户的账单使用早于该日期,可以传入开始日期startDate
    Billing posibleStartBilling = OpenAiUtils.billing("2022-01-01");
    log.info("可能的历史账单信息:{}", posibleStartBilling);
}

测试结果如下:

四月使用:0.9864320000000001美元
一共使用:2.2074280000000002美元
自从2023/01/31使用:2.2074280000000001美元
订阅信息(包含到期日期,账户总额度等信息):Subscription(object=billing_subscription, hasPaymentMethod=false, canceled=false, canceledAt=null, delinquent=null, accessUntil=1688169600, softLimit=66667, hardLimit=83334, systemHardLimit=83334, softLimitUsd=4.00002, hardLimitUsd=5.00004, systemHardLimitUsd=5.00004, plan=Plan(title=Explore, id=free), accountName=Leo Mikey, poNumber=0, billingEmail=null, taxIds=null, billingAddress=null, businessAddress=null)
历史账单信息:Billing(dueDate=2023-07-01, total=5.00004, usage=2.2074280000000002, balance=2.7926119999999998)
可能的历史账单信息:Billing(dueDate=2023-07-01, total=5.00004, usage=2.2074280000000002, balance=2.7926119999999998)

2.6 自定义baseUrl

由于部分原因,用户可以选择搭建好的代理服务作为baseUrl,而不需要使用官方的https://api.openai.com/

这部分可以和第4节放在一起看

如果用户基于SpringBoot使用自定义baseUrl,可以参考下面的application.yml,配置chatgpt.base-url,如果不配置,则表示默认使用官方的https://api.openai.com/

chatgpt:
  token: sk-xxxxxxxxxxxxxxxxxx
  proxy-host: 127.0.0.1 #可选
  proxy-port: 7890 #可选
  model: text-davinci-003 #可选
  chat-model: gpt-3.5-turbo #可选
  retries: 10 #可选,默认为5
  session-expiration-time: 30 #可选,不填则会话永不过期
  base-url: https://openai.api2d.net/ #可选,默认为https://api.openai.com/

然后相关调用代码如下:

@PostMapping("/baseUrl")
public void baseUrl() {
    // 先在application.yml中配置chatgpt.base-url
    System.out.println("models列表:" + OpenAiUtils.listModels());
}

如果用户不想和Spring集成,则可以使用main方法调用的方式。

下面为了测试方便性,均以main方法做示例,结合SpringBoot使用OpenAiUtils调用的方法请参考github上的demo示例。

main方法中直接使用请参考第3节扩展部分自定义OpenAiProxyService

如下所示:

public static void main(String[] args) {
    ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
        .proxyHost("127.0.0.1")
        .proxyPort(7890)
        // 自定义baseUrl,若不需要自定义baseUrl,下一行可以去掉
        .baseUrl("https://openai.api2d.net/")
        .build();
    OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
    System.out.println("models列表:" + openAiProxyService.listModels());
}

其中自定义了baseUrl为 https://openai.api2d.net/ ,并调用了listModels方法。

2.7 模型(model)

提供了两个与模型相关的方法:

// 列出全部models
List<Model> listModels() {...}
// 获取指定model信息
Model getModel(String model) {...}

测试代码如下:

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
    .proxyHost("127.0.0.1")
    .proxyPort(7890)
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
System.out.println("models列表:" + openAiProxyService.listModels());
System.out.println("=============================================");
System.out.println("text-davinci-003信息:" + openAiProxyService.getModel("text-davinci-003"));

测试结果(截取如下):

models列表:[Model(id=whisper-1, object=model, ownedBy=openai-internal, permission=[Permission(id=modelperm-KlsZlfft3Gma8pI6A8rTnyjs,.....
=============================================
text-davinci-003信息:Model(id=text-davinci-003, object=model, ownedBy=openai-internal, permission=[Permission(id=modelperm-jepinXYt59ncUQrjQEIUEDyC, object=model_permission, created=1688551385, allowCreateEngine=false, allowSampling=true, allowLogProbs=false, allowSearchIndices=false, allowView=true, allowFineTuning=false, organization=*, group=null, isBlocking=false)], root=text-davinci-003, parent=null)

2.8 编辑(edit)

edit功能可以用来检查语句的拼写错误,代码的正确性等,api中重载了多种edit相关的方法,这里介绍最简单的一种,其他重载方法,可通过源码查看。

String edit(String input, String instruction) {...}

测试代码如下:

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
    .proxyHost("127.0.0.1")
    .proxyPort(7890)
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
String input = "What day of the wek is it?";
String instruction = "Fix the spelling mistakes";
System.out.println("编辑前:" + input);
// 下面这句和openAiProxyService.edit(input, instruction, EditModelEnum.TEXT_DAVINCI_EDIT_001);是一样的,默认使用模型TEXT_DAVINCI_EDIT_001
System.out.println("编辑后:" + openAiProxyService.edit(input, instruction));
System.out.println("=============================================");
input = "    public static void mian([String] args) {\n" +
    "        system.in.println(\"hello world\");\n" +
    "    }";
instruction = "Fix the code mistakes";
System.out.println("修正代码前:\n" + input);
System.out.println("修正代码后:\n" + openAiProxyService.edit(input, instruction, EditModelEnum.CODE_DAVINCI_EDIT_001));

测试结果如下:

编辑前:What day of the wek is it?
编辑后:What day of the week is it today?

=============================================
修正代码前:
    public static void mian([String] args) {
        system.in.println("hello world");
    }
修正代码后:
    public static void main(String[] args) {
        System.out.println("hello world");
    }

2.9 向量(embeddings)

有时候需要将文本内容转化成计算机可以识别到的向量形式,可以使用这个方法。api中重载了多种embeddings相关的方法,这里介绍最简单的一种,其他重载方法,可通过源码查看。

List<Double> embeddings(String text) {...}

测试代码如下:

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
    .proxyHost("127.0.0.1")
    .proxyPort(7890)
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
// 单文本
String text = "Once upon a time";
System.out.println("文本:" + text);
System.out.println("文本的嵌入向量:" + openAiProxyService.embeddings(text));
System.out.println("=============================================");
// 文本数组
String[] texts = {"Once upon a time", "There was a princess"};
System.out.println("文本数组:" + Arrays.toString(texts));
EmbeddingRequest embeddingRequest = EmbeddingRequest.builder()
    .model(EmbeddingModelEnum.TEXT_EMBEDDING_ADA_002.getModelName()).input(Arrays.asList(texts)).build();
System.out.println("文本数组的嵌入向量:" + openAiProxyService.embeddings(embeddingRequest));

测试结果截取如下:

文本:Once upon a time
文本的嵌入向量:[0.02122687, -0.02013961, -0.004614537, -0.011599556, -0.011213957, 0.0019817224, 0.0023404553, ...
文本数组:[Once upon a time, There was a princess]
文本数组的嵌入向量:EmbeddingResult(model=text-embedding-ada-002-v2, object=list, data=[Embedding(object=embedding, embedding=[0.02122687, -0.02013961, -0.004614537, -0.011599556, -0.011213957, 0.0019817224...

2.10 转录(transcription)

转录功能可以将诸如mp3mp4等格式的音频文件转化成文字输出。

最简单的一种使用方法如下,其他重载方法可通过代码查看。

String transcription(String filePath, AudioResponseFormatEnum audioResponseFormatEnum) {...}

下面是一首许嵩的《想象之中》的转录测试。

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
    .proxyHost("127.0.0.1")
    .proxyPort(7890)
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
System.out.println("语音文件转录后的json文本是:" + openAiProxyService.transcription(filePath, AudioResponseFormatEnum.JSON));
// File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
// System.out.println("语音文件转录后的json文本是:" + openAiProxyService.transcription(file, AudioResponseFormatEnum.JSON));
    

测试结果截取如下:

语音文件转录后的json文本是:{"text":"想像之中 想像之中 雨過一道彩虹 抬起了頭 瑟瑟灰色天空 想像之中 付出會有結果 毫無保留 信奉你的承諾 想像之中 這次要愛很久 我領略過 你眼裡的溫柔 熱戀以後 你忽然的冰凍 但若兩人丟給我去承受 想像中 很不同 想像中 一切都和後來不同 我承認 曾經那麼心動 你沒想像中那麼戀舊 回憶換不回你的溫柔 最後也不是故作冷漠 轉過頭 我怎麼又一滴淚落 我沒想像中那麼脆弱 分開後 形容也沒消瘦 一起踏過了幾座春秋 過了愛不是追逐佔有 想像之中 這次要愛很久 我領略過 你眼裡的溫柔 熱戀以後 你忽然的冰凍 但若兩人丟給我去承受 想像中 很不同 想像中 一切都和後來不同 我承認 曾經那麼心動 你沒想像中那麼戀舊 回憶換不回你的溫柔 最後也不是故作冷漠 轉過頭 我怎麼又一滴淚落 我沒想像中那麼脆弱 分開後 形容也沒消瘦 一起踏過了幾座春秋 過了愛不是追逐佔有 你沒想像中那麼戀舊 回憶換不回你的溫柔 最後也不是故作冷漠 轉過頭 我怎麼又一滴淚落 我沒想像中那麼脆弱 分開後 形容也沒消瘦 一起踏過了幾座春秋 領悟了愛不是追逐佔有"}

上述文字输出的时候是繁体,具体使用可以使用重载方法中一个代码TranscriptionRequest入参的方法,其中的language可以指定返回的语言,具体使用可以前往官网查看。

2.11 翻译(translation)

翻译功能可以将任意的音频文件翻译成英文。

最简单的一种使用方法如下,其他重载方法可通过代码查看。

String translation(String filePath, AudioResponseFormatEnum audioResponseFormatEnum) {...}

下面是一首许嵩的《想象之中》的翻译测试。

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxx")
    .proxyHost("127.0.0.1")
    .proxyPort(7890)
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
String filePath = "src/main/resources/audio/想象之中-许嵩.mp3";
System.out.println("语音文件翻译成英文后的json文本是:" + openAiProxyService.translation(filePath, AudioResponseFormatEnum.JSON));
// File file = new File("src/main/resources/audio/想象之中-许嵩.mp3");
// System.out.println("语音文件翻译成英文后的json文本是:" + openAiProxyService.translation(file, AudioResponseFormatEnum.JSON));
    

测试结果截取如下:

语音文件翻译成英文后的json文本是:{"text":"Translated by Hua Chenyu English Subs In my imagination, there will be results Without keeping your promise In my imagination, this time I will love for a long time I have experienced the tenderness in your eyes After falling in love, you suddenly freeze But if two people are thrown to me to bear In my imagination, it's very different In my imagination, everything is different from the later I admit that I used to be so heartbroken You are not as long-lasting as I imagined Memories can't exchange your tenderness In the end, it's not lonely and lonely Why am I crying again? ..."}

3. 扩展

3.1 自定义OpenAiProxyService

由于之前的版本中使用@Bean的方式初始化OpenAiProxyServiceOpenAiUtils,导致一个
SpringBoot中实例是唯一的。

但是有时候需要在项目里自定义多个OpenAiProxyService实例,来装配不同的ChatGPTProperties信息(可以实例化多个Token(sk-xxxxxxxxxxx)使用)。

所以在1.1.6版本中新增了自定义OpenAiProxyService功能。在维持原有SpringBoot项目中全局的一个OpenAiUtils实例的基础上,现在可以自定义不同的OpenAiProxyService实例,并且实例之间的属性是完全隔离的。

下面是一个Demo用来展示使用方法。

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-002xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
    .proxyHost("127.0.0.1")// 代理ip
    .proxyHost("7890")// 代理端口
    .build();
OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
// 直接使用new出来的openAiProxyService来调用方法,每个OpenAiProxyService都拥有自己的Token。
// 这样在一个SpringBoot项目中,就可以有多个Token,可以有更多的免费额度供使用了
openAiProxyService.createStreamChatCompletion("Java的三大特性是什么");

在上述方法中,新new了一个ChatGPTProperties对象,并且set了tokensk-002xxxxxxxxxxxxxxxxxxxxxxxxx(这里不需要设置除了tokenproxyHostproxyPort以外的其他属性,因为ChatGPTProperties的其他属性拥有默认值,如果需要对其他属性做修改,可以自行设置。注意:sessionExpirationTime没有默认值,表示会话没有过期时间,如果需要设置会话过期时间,请set该值。

而在application.yml中设置的tokensk-001xxxxxxxxxxxxxxxxxxxxxxxxx,这个token是给全局唯一的OpenAitils用的,这样就可以通过OpenAiProxyService的构造方法new出来一个新的OpenAiProxyService实例,其中构造方法的第二个参数直接填Duration.ZERO就好,表示Http调用请求没有超时时间,后续版本更新中,我会新增一个只有一个入参的构造方法。

这样直接使用new出来的openAiProxyService来调用方法,每个OpenAiProxyService都拥有自己的Token。

在一个SpringBoot项目中,就可以有多个Token,可以有更多的免费额度供使用了。

4 自定义OpenAi服务端

4.1 介绍

默认使用OpenAi服务的baseUrlhttps://api.openai.com/ ,众所周知的原因,很多人网络无法直连。如果服务是部署在国外服务器,或者拥有代理,配置proxy-hostproxy-port依旧可以直接访问 https://api.openai.com/ ,这个也是比较推荐的方式,毕竟这是直连openai的服务,最安全,提供的服务接口也最全。

但是很多人没用国外的服务器,也没有自己的代理,在这里提供一种解决方法,通过使用这种方法,来实现无需国外服务器,也无需代理即可访问OpenAi服务。

在这里先简单介绍一下反向代理的概念。

反向代理是一种用于隐藏服务端真实地址的技术。比如说真实地址是B,但是我们不希望用户直连B,让B的地址暴露,我们就可以使用反向代理,让A的请求转发到B。

在这里,我们可以将B理解成 https://api.openai.com/ ,由于我们不能直连 https://api.openai.com/ ,但是我们可以在一台可以直连 https://api.openai.com/ 的服务器上反向代理,给我们暴露一个网址A,我们将baseUrl设置为A,向A发送请求,服务器接受到后自动将请求转发到 https://api.openai.com/ ,并且将结果返回。这样的话,我们只需要指定baseUrl,都不需要设置代理proxy-hostproxy-port即可访问OpenAi的服务了。那现在的问题就是怎么简单的获取到这样的一个baseUrl了。

这里我找到了一个可以提供OpenAi代理的服务。访问https://www.ichati.cn/dashboard/products/tools,注册登录后选择白泽,点击立即使用。

image-20230718104126500

然后输入API-Key,点击开始部署(这里我测试只需要长度保持一致,后面几位的api-key可以随便填)。

image-20230718104238571

然后如下图所示生成了我们的baseUrl(注意,这里生成的网页最后没用斜杠/,使用服务的使用baseUrl需要在最后带/,例如 https://apps.ichati.cn/0a7699ac-bc73-4732-9263-55c1c01f56e3/ )。

image-20230718104412550

4.2 测试

在4.1节中已经生成了我们项目的baseUrl,且因为它给我们转发,我们可以任务,他的服务部署在一台可以直连 https://api.openai.com/ 的服务器上,也就自然不需要设置proxy-hostproxy-port了。

4.2.1 基于SpringBoot使用OpenAiUtils

先设置application.yml,只需要设置tokenbaseUrl即可。

image-20230718111150784

启动SpringBoot项目。

使用Post工具调用下面的接口生成图片。

@PostMapping("/createImage")
public List<String> createImage(String prompt) {
        List<String> imageList = OpenAiUtils.createImage(prompt);
        System.out.println(imageList);
        return imageList;
        }

image-20230718115911134

结果如下:

image-20230718115949339

访问该链接,就可以得到一个生成的图片~

4.2.2 基于Main方法使用OpenAiProxyService

下面是使用Main方法进行调用的具体代码,只需要设置tokenbaseUrl即可。

ChatGPTProperties properties = ChatGPTProperties.builder().token("sk-xxxx")
        .baseUrl("https://apps.ichati.cn/0a7699ac-bc73-4732-9263-55c1c01f56e3/") // 这里需要填自己的baseUrl
        .build();
        OpenAiProxyService openAiProxyService = new OpenAiProxyService(properties);
        System.out.println(openAiProxyService.chatCompletion("Go写个Hello World程序"));

输入结果如下:

[package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}]

5 AI助手展示

接入微信公众号,AI助手可自动回复。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/789086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python TypeError: unsupported operand type(s) for +: ‘int‘ and ‘str‘

在键入数值进行相加运算时&#xff0c;报了这样一个错误 类型错误&#xff1a;不支持操作类型为整数和字符串 错误分析&#xff1a;sumsuminput() 未被系统识别&#xff0c;导致程序错误 解决方法&#xff1a;给键入的数值定义&#xff0c;声明为整数 sumsumint(input()) 即…

开源大模型LLaMA 2会扮演类似Android的角色么?

在AI大模型没有商业模式&#xff1f;等文章中&#xff0c;我多次表达过这样一个观点&#xff1a;不要把大模型的未来应用方式比喻成公有云&#xff0c;大模型最终会是云端操作系统的核心&#xff08;新通用计算平台&#xff09;&#xff0c;而它的落地形式会很像过去的沃森&…

消息队列(一)-- RabbitMQ入门(3)

延迟队列 延迟队列是用来存放需要在指定时间被处理的元素的队列。队列内部是有序的&#xff0c;最重要的特性就体现在它的延时属性上。 使用场景&#xff1a;订单在十分钟之内未支付则自动取消。 整合SpringBoot 新建一个SpringBoot的Module 添加依赖 <!--RabbitMQ依赖-…

Kafka集群——(区别于Master/Slave架构的的分布式集群)

Kafka角色介绍&#xff1a; 1. Producer:消息生产者&#xff1a; 2. Broker: kafka实例&#xff0c;可以理解为一台kafka服务器&#xff0c;kafka cluster 是由多个broker构成的集群。 3. Topic: 消息主题&#xff0c;理解为消息队列&#xff0c;kafka数据就保存在topic里。…

vue+Element-ui实现树形组件、表格树

需求 要做出如下图所示的 树形表格&#xff0c;也就是数据之间有父子类关系的这种&#xff0c;可以点击展开、收缩 像上图这样的表格树 实现 1.使用树形组件 在学习树形表格之前&#xff0c;肯定得先搞懂普通的树形组件是怎么搞的&#xff0c;然后将其套到表格中就好了&…

基于JavaSE的手机库存管理系统

1、项目背景 基于JavaSE完成如下需求&#xff1a; 功能需求&#xff1a; 1、查询库存量 2、可以修改库存中不同品牌手机的个数 3、退出系统 实现步骤&#xff1a; 1、把List当做库房 2、把手机存放在库房中 3、使用封装的方法区操作仓库中的手机 2、项目知识点 面向对象 集合…

存储过程——case函数、while函数、repeat函数的应用、loop函数

1.case循环 存储过程中&#xff0c;几种循环格式的语法讲解。 create procedure p6(in month int) begindeclare result varchar(10);casewhen month > 1 and month < 3 thenset result : 第一季度;when month > 4 and month < 6 thenset result : 第二季度;whe…

spring cloud sentinel

初始时 并不能将sentinel实例显示出来的 需要修改加配置clent-ip spring:cloud:sentinel:transport:#本机ipclient-ip: 192.168.10.108#dashboard服务端的grp端口 监听心跳的port: 8719#dashboard服务端地址dashboard: 192.168.12.14:8080

数据结构【栈和队列】

第三章 栈与队列 一、栈 1.定义&#xff1a;只允许一端进行插入和删除的线性表&#xff0c;结构与手枪的弹夹差不多&#xff0c;可以作为实现递归函数&#xff08;调用和返回都是后进先出&#xff09;调用的一种数据结构&#xff1b; 栈顶&#xff1a;允许插入删除的那端&…

了解Unity编辑器之组件篇Physics(四)

Physics&#xff1a;用于处理物理仿真和碰撞检测。它提供了一组功能强大的工具和算法&#xff0c;用于模拟真实世界中的物理行为&#xff0c;使游戏或应用程序更加真实和可信。 主要用途包括&#xff1a; 碰撞检测&#xff1a;Unity Physics 提供了高效的碰撞检测算法&#x…

【Unity实战篇 】| 游戏中实现镂空遮罩效果【矩形、圆形镂空遮罩】

前言【Unity实战篇 】 | 游戏中实现镂空遮罩效果【矩形、圆形镂空遮罩】一、制作原理二、矩形中间镂空遮罩效果2.1 实现镂空显示2.2 镂空区域内事件穿透三、圆形中间镂空遮罩效果总结前言 本文来写一下怎样在Unity中完成一个 镂空遮罩 的效果。镂空遮罩 比较常用的有两种:矩形…

力扣题库刷题笔记73--矩阵置零

1、题目如下&#xff1a; 2、个人Python代码实现 3、个人Python代码思路 a、声明2个空数组p、q&#xff0c;用于存放值为0的元素matrix[i][j]的下标 b、首先遍历二维数组matrix&#xff0c;找到值为0的元素matrix[i][j]&#xff0c;将下标i加入数组p&#xff0c;将下标j加入数…

【【51单片机直流电机调速】】

学会电机调速&#xff0c;掌握中国速度 PWM的生成方法 先用户设定一个比较值&#xff0c;然后计数器定时自增。 当计数器<比较值&#xff0c;输出0 当计数器>比较值&#xff0c;输出1 main.c #include <REGX52.H> #include"delay.h" #include"…

vue 学习笔记 【ElementPlus】el-menu 折叠后图标不见了

项目当前版本 {"dependencies": {"element-plus/icons-vue": "^2.1.0","types/js-cookie": "^3.0.3","types/nprogress": "^0.2.0","axios": "^1.4.0","core-js": &quo…

PHP 药店管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 药品管理系统 是一套完善的web设计系统,系统采用smarty框架进行开发设计&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 PHP 药店管理系统mysql数据库web结构apache计 下载地址…

TSN -促进IT/OT 融合的网络技术

时间敏感网络&#xff08;tsn&#xff09;技术是IT/OT 融合的一项关键的基础网络技术&#xff0c;它实现了在一个异构网络中&#xff0c;实现OT的实时数据和IT系统的交互数据的带宽共享。 TSN允许将经典的高确定性现场总线系统和IT应用&#xff08;如大数据传输&#xff09;的功…

第1章 获取数据库中的数据

CoreShop源程序是以数据库优先进行定义的&#xff0c;所以其本身不包含代码优先的定义&#xff0c;但本从更习惯于代码优先&#xff0c;所以为其定义了代码优先的定义。 1 CoreCms.Net.Model.Entities.SysRole using SqlSugar; using System.ComponentModel.DataAnnotations…

力扣 56. 合并区间

题目来源&#xff1a;https://leetcode.cn/problems/merge-intervals/description/ C题解&#xff1a;根据左区间排序&#xff0c;更新每一段的右区间最大值&#xff0c;直到间断。 class Solution { public:static bool cmp(vector<int> & a, vector<int> &a…

小学期笔记——天天酷跑5

效果&#xff1a; -------------------------- 效果&#xff1a; ------------------ 让人物跑起来 效果&#xff1a;&#xff08;此时的人物是运动的&#xff09; ---------------------- 通过键盘控制角色 效果&#xff1a; 人物可以上左右动&#xff0c;自由落体

K8S初级入门系列之二-集群搭建

一、前言 为了更好学习K8S&#xff0c;建议自行搭建一套K8S的环境&#xff0c;目前比较流行的有两种搭建工具&#xff0c;一种是单机版的minkube&#xff0c;一种是集群版的kubeadm。minkube更多是用于实验环境&#xff0c;且单机版隐藏了很多细节&#xff0c;而kubeadm更贴近实…