目录
源码及更详细的介绍说明参见Git上的ReadME.md文档
https://github.com/asleepyfish/chatgpt
本文结合SpringBoot的Demo地址:https://github.com/asleepyfish/chatgpt-demo
流式输出结合Vue的Demo地址:https://github.com/asleepyfish/chatgpt-vue
注意:流式输出在2.4节,请仔细阅读到最后。
如果能对你有所帮助,Github帮忙star⭐一下,点赞收藏,谢谢~
版本更新说明
- 1.1.5 增加查询账单功能
billingUsage
(单位:美元),可以选择传入开始和结束日期查询(最多100天),或者不传入参,此时表示查询所有日期账单。 - 1.1.6 增加自定义
OpenAiProxyService
功能,支持单个SpringBoot中添加多个OpenAiProxyService
实例,每个实例可以拥有个性化的参数;查询账单功能优化。
1. 配置阶段
1.1 依赖引入
pom.xml
中引入依赖(当前最新版本为1.1.6,可前往Github页面查看当前最新版本)
<dependency>
<groupId>io.github.asleepyfish</groupId>
<artifactId>chatgpt</artifactId>
<version>1.1.6</version>
</dependency>
1.2 配置application.yml文件
Configurer les paramètres associés dans application.yml
le fichier (Facultatif est un paramètre facultatif)
paramètre | expliquer |
---|---|
jeton | CLES D'API D'APPLICATION |
Hôte proxy | Adresse IP |
port proxy | port proxy |
modèle (facultatif) | Le modèle peut être rempli ou non, la valeur par défaut est text-davinci-003 |
modèle de chat (facultatif) | Il peut être rempli ou non, et la valeur par défaut est gpt-3.5-turbo (ChatGPT est actuellement le modèle le plus puissant, et ce modèle est utilisé pour générer des réponses) |
tentatives (facultatif) | Il fait référence au nombre de nouvelles requêtes lorsque la requête chatgpt échoue pour la première fois (la raison de l'augmentation de ce paramètre est qu'en raison d'un grand nombre de visites, à un certain moment, le service chatgpt sera inaccessible. 5) |
heure d'expiration de la session (facultatif) | (unité (min)) est la durée pendant laquelle la session sera détruite après aucun accès. Lorsque cette valeur n'est pas renseignée, cela signifie que toutes les questions et réponses sont sous la même session, et la session du même utilisateur ne sera jamais détruite ( augmentation de la consommation de requêtes) |
exemple:
chatgpt:
token: sk-xxxxxxxxxxxxxxx
proxy-host: 127.0.0.1
proxy-port: xxxx
session-expiration-time: 30
Parmi eux, jeton, hôte proxy et port proxy sont requis
Le paramètre session-expiration-time ci-dessus est très important, il est utilisé pour indiquer combien de temps la session sera détruite après aucun accès, afin d'avoir un dialogue continu en rapport avec le contexte.
La méthode de mise en œuvre consiste à distinguer une session via l'utilisateur dans ChatCompletionRequest, et le temps d'expiration de session indique combien de temps la session sera détruite après aucun accès.
Si vous ne comprenez pas ici, veuillez consulter l'exemple de la section 2.1
1.3 Ajouter des annotations
L'ajout des annotations de la figure à la classe de démarrage injectera le service dans Spring.
2. utiliser
2.1 Générer des réponses
Une classe d'outils est fournie OpenAiUtils
, qui fournit des méthodes associées pour l'appel.
Le plus simple d'entre eux à utiliser est :
OpenAiUtils.createChatCompletion(content);// 不建议使用
Le content
paramètre d'entrée est la chaîne de la question saisie. Mais pas recommandé.
Il est recommandé d'utiliser ici la méthode suivante : En passant la valeur de l'utilisateur et en combinant session-expiration-time
les paramètres, il est possible de spécifier une certaine session ou un dialogue continu d'un certain utilisateur.
OpenAiUtils.createChatCompletion(content, user);// 建议使用
Fournit également une méthode statique générique qui est
public static List<String> createChatCompletion(ChatCompletionRequest chatCompletionRequest) {
...}
Les paramètres d'entrée ChatCompletionRequest
contiennent certains paramètres ajustables du modèle.
OpenAiUtils
La classe fournit également un certain nombre de méthodes statiques facultatives, qui peuvent être visualisées par vous-même.
Le paramètre de retour de la méthode ci-dessus est une liste, car la réponse renvoyée en ajustant le paramètre n
peut renvoyer plusieurs réponses différentes à la fois ( un paramètre n
de ChatCompletionRequest
la classe).
2.1.1 Tests
Code d'essai :
@PostMapping("/chatTest")
public List<String> chatTest(String content) {
return OpenAiUtils.createChatCompletion(content, "testUser");
}
Publier une demande
Entrée de paramètre : méthode de sérialisation Java
résultat de retour :
[
"\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接口的序列化可以更加灵活地控制序列化的过程。"
]
Entrez à nouveau : existe-t-il un cadre de sérialisation plus efficace ?
résultat de retour :
[
"是的,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自带的序列化方式更快、更小、更灵活,可以根据具体的需求选择合适的框架。"
]
On peut voir que les deux questions et réponses ci-dessus sont dans une session, et les paramètres mentionnés ci-dessus sont session-expiration-time
le temps de destruction lorsque la session représentée par cet utilisateur n'a pas été consultée depuis longtemps. unité (min)
2.2 Générer des images
La façon la plus simple d'utiliser est
OpenAiUtils.createImage(prompt);
Le paramètre d'entrée représente le texte de description de l'image générée et fournit également une méthode statique générale
public static List<String> createImage(CreateImageRequest createImageRequest) {
...}
CreateImageRequest
Certains paramètres peuvent être utilisés dans les paramètres d'entrée , qui n
indiquent le nombre d'images générées et responseFormat
le format des images générées. Le format est divisé en url
deux b64_json
types. Si vous souhaitez renvoyer une URL, l'URL renvoyée disparaîtra et heure après sa génération. La valeur par défaut est url
.
2.2.1 Tests
code d'essai
@Test
public void testGenerateImg() {
OpenAiUtils.createImage("英短").forEach(System.out::println);
}
Résultats
Par défaut, une url sera générée, cliquez pour voir l'image.
2.3 Télécharger des images
Optimisé sur la base de 3.2, utilisez-le directement responseFormat
, b64_json
puis analysez-le en une image et renvoyez-le. L'utilisation simple est la suivante :
OpenAiUtils.downloadImage(prompt, response);
La méthode générale est la suivante :
public static void downloadImage(CreateImageRequest createImageRequest, HttpServletResponse response) {
...}
Lorsque CreateImageRequest
le paramètre de retour défini dans l'objet n
est supérieur à 1, l'image sera compressée dans un package zip et renvoyée, et lorsqu'il n
est égal à 1, l'image sera renvoyée directement.
2.3.1 Tests
code d'essai
@RestController
public class ChatGPTController {
@GetMapping("/downloadImage")
public void downloadImage(String prompt, HttpServletResponse response) {
OpenAiUtils.downloadImage(prompt, response);
}
}
Envoyez une demande d'obtention, puis sélectionnez Envoyer et télécharger
我用的get 工具是idea里面下载的插件Fast Request的,用Postman也是可以的,但是要选择 Send and Download,上图中绿色的箭头是Send,蓝色的是Send and Download。
2.4 生成流式回答
生成流式回答的方法是OpenAiUtils
的createStreamChatCompletion
方法,本工具类重载了同名的多个参数的方法,其中最通用的方法是
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
方法的入参中。同时,为了避免结果输出到浏览器产生乱码和支持流式输出,需要ContentType
和CharacterEncoding
。
具体代码如下:
@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) {
...}
其中startDate
和endDate
区间范围不超过100天。
第二个方法是查询从2022年1月1日
距今的账单的方法:
public String billingUsage() {
...}
2.5.1 测试
测试代码如下:
@GetMapping("/billingUsage")
public void billingUsage() {
String monthUsage = OpenAiUtils.billingUsage("2023-04-01", "2023-05-01");
System.out.println("四月使用:" + monthUsage + "美元");
String totalUsage = OpenAiUtils.billingUsage();
System.out.println("一共使用:" + totalUsage + "美元");
}
测试结果如下:
四月使用:0.9864320000000001美元
一共使用:1.120594美元
3. 扩展
3.1 自定义OpenAiProxyService
由于之前的版本中使用@Bean的方式初始化OpenAiProxyService
和OpenAiUtils
,导致一个SpringBoot中实例是唯一的。
但是有时候需要在项目里自定义多个OpenAiProxyService
实例,来装配不同的ChatGPTProperties
信息(可以实例化多个Token(sk-xxxxxxxxxxx)使用)。
所以在1.1.6
版本中新增了自定义OpenAiProxyService
功能。在维持原有SpringBoot项目中全局的一个OpenAiUtils
实例的基础上,现在可以自定义不同的OpenAiProxyService
实例,并且实例之间的属性是完全隔离的。
下面是一个Demo用来展示使用方法。
@GetMapping("/customToken")
public void customToken() {
ChatGPTProperties chatGPTProperties = new ChatGPTProperties();
chatGPTProperties.setToken("sk-002xxxxxxxxxxxxxxxxxxxxxxxxx");
chatGPTProperties.setProxyHost("127.0.0.1");
chatGPTProperties.setProxyPort(7890);
OpenAiProxyService openAiProxyService = new OpenAiProxyService(chatGPTProperties, Duration.ZERO);
// 直接使用new出来的openAiProxyService来调用方法,每个OpenAiProxyService都拥有自己的Token。
// 这样在一个SpringBoot项目中,就可以有多个Token,可以有更多的免费额度供使用了
openAiProxyService.createStreamChatCompletion("Java的三大特性是什么");
}
Dans la méthode ci-dessus, un nouvel ChatGPTProperties
objet est nouvellement créé et il est défini token
comme sk-002xxxxxxxxxxxxxxxxxxxxxxxxx
(ici, il n'est pas nécessaire de définir d'autres attributs sauf token
, proxyHost
et , car les autres attributs ont des valeurs par défaut. Si vous devez modifier d'autres attributs, vous pouvez définir Remarque : il n'y a pas de valeur par défaut pour sessionExpirationTime, ce qui signifie que la session n'a pas de délai d'expiration. Si vous devez définir le délai d'expiration de la session, veuillez définir cette valeur. )proxyPort
ChatGPTProperties
Et application.yml
défini dans token
, sk-001xxxxxxxxxxxxxxxxxxxxxxxxx
ce jeton est le seul pour le monde entier OpenAitils
, de sorte qu'une OpenAiProxyService
nouvelle OpenAiProxyService
instance peut être générée via la nouvelle méthode de construction, et le deuxième paramètre de la méthode de construction Duration.ZERO
peut être rempli directement, indiquant que la demande d'appel Http n'a pas timed out Avec le temps, dans les mises à jour de version ultérieures, j'ajouterai une méthode de construction avec un seul paramètre d'entrée.
De cette façon, vous pouvez directement utiliser le nouveau openAiProxyService
pour appeler la méthode, chacune OpenAiProxyService
ayant son propre Token.
Dans un projet SpringBoot, il peut y avoir plus d'un Token
et plusieurs quotas gratuits peuvent être utilisés.
3 affichage de l'assistant IA
Accédez au compte officiel WeChat et l'assistant AI peut répondre automatiquement.