摸鱼太久没写博客已经不知道怎么写了。

Kotlin使用okhttp3对OpenAI的API的调用示例

先在这里放一个官方文档

我们需要关注的三个关键参数是: API的地址、API的KEY、构建的消息块

https://api.openai.com/v1/chat/completions

Bearer sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

1
2
3
4
5
{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Say this is a test!"}],
"temperature": 0.7
}

原始的curl示例

1
2
3
4
5
6
7
8
curl https://api.openai.com/v1/chat/completions 
-H "Content-Type: application/json"
-H "Authorization: Bearer $OPENAI_API_KEY"
-d '{
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Say this is a test!"}],
"temperature": 0.7
}'

先使用Headers.headersOf()构建一个Header信息,把‘Content-Type’和‘Authorization’的内容放进去

1
val headers = Headers.headersOf("Authorization","Bearer sk-xxxxxx","Content-Type","application/json;charset=utf-8")

在使用fastjson2来构建要发送的消息体,其中包含提问的模型信息和交流的问题等各类参数。

1
2
var chatMsgInfo = JSONArray.of(JSONObject.of("role", "user", "content", "Say this is a test!"))
var chatInfo = JSONObject.of("model", "gpt-3.5-turbo", "messages", chatMsgInfo)

构建request请求体,选择POST方式把URL,Header和param放入请求体中。为JSONObject类型的chatInfo需要转化一下类型:chatInfo.toString().toRequestBody(“application/json;charset=utf-8”.toMediaType())

1
val request = Request.Builder().url("https://api.openai.com/v1/chat/completions").headers(headers).post(chatInfo.toString().toRequestBody("application/json;charset=utf-8".toMediaType())).build()

构建请求客户端,与通常不同的是由于网络因素,需要配置代理和请求超时等。

1
2
3
4
5
var client = OkHttpClient.Builder() //设置代理 .proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 8080)))
.connectTimeout(60L, TimeUnit.SECONDS) //连接超时
.readTimeout(60L, TimeUnit.SECONDS) //读超时
.writeTimeout(60L, TimeUnit.SECONDS) //写超时
.build()

发起请求和解析返回的消息体,client.newCall(request).execute()直接请求,返回的格式体如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"id": "chatcmpl-xxxxxxx",
"object": "chat.completion",
"created": 1690959173,
"model": "gpt-3.5-turbo-0613",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "This is a test!"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 13,
"completion_tokens": 5,
"total_tokens": 18
}
}

需要的内容为choices.message.content 里面的内容。解析过程如下

1
2
3
4
5
6
7
8
9
10
11
try {
val response = client.newCall(request).execute()
var content = JSONObject.parseObject(response.body!!.string()).getJSONArray("choices").getJSONObject(0)
.getJSONObject("message").getString("content").toString()
println(content)
if(response.body != null) {
response.body!!.close();
}
}catch (e: IOException){
println(e.message)
}

以上就是使用okhttp简单的访问OpenAPI的过程。

接入miraibot

贴代码吧,没什么区别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
msg.startsWith("#chat") ->{
val chatMsg = msg.substring(5, msg.length)
val OPENAPI = "https://api.openai.com/v1/chat/completions"
val headers = Headers.headersOf("Authorization","Bearer sk-xxx","Content-Type","application/json;charset=utf-8")
//val localhostProxy: Proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 10808))
// "{ "model": "gpt-3.5-turbo","messages": [{"role": "user", "content": "Hello!"}] }"
var chatMsgInfo = JSONArray.of(JSONObject.of("role", "user", "content", chatMsg))
var chatInfo = JSONObject.of("model", "gpt-3.5-turbo", "messages", chatMsgInfo)
println(chatInfo.toString())
val request = Request.Builder().url(OPENAPI).headers(headers).post(chatInfo.toString().toRequestBody("application/json;charset=utf-8".toMediaType())).build()
var client = OkHttpClient.Builder() //设置代理
.connectTimeout(60L, TimeUnit.SECONDS) //连接超时
.readTimeout(60L, TimeUnit.SECONDS) //读取超时
.writeTimeout(60L, TimeUnit.SECONDS) //写超时
.build()
try {
val response = client.newCall(request).execute()
var content = JSONObject.parseObject(response.body!!.string()).getJSONArray("choices").getJSONObject(0)
.getJSONObject("message").getString("content").toString()
this.group.sendMessage(message.quote() + At(sender.id) + content )
//LangBaoServer.updateMoney(sender.id,-1,sender.id)
if(response.body != null) {
response.body!!.close();
}
}catch (e: IOException){
println(e.message)
}
}
//这里是at机器人触发所以是机器人的qq号
msg.startsWith("@22") ->{
//用redis来缓存prompt数据
val jedis: Jedis = Jedis("xxx.xxx.xxx.xxx",6379)
jedis.auth("xxxxxx")
//Todo 存在Bug 当Redis数据库没有数据时 get方法会抛出异常, 处理方案 1.判断并使用set赋予初始值[-]; 2.在用户注册时同时初始化Redis数据库; 3.设置指令初始化账户 也可用于prompt数据
val prompt = jedis.get(group.id.toString()+"-"+ sender.id.toString()).substring(1).dropLast(1)
//这里的substring(11, msg.length)的11是因为前面有10位都是QQ号
val chatMsg = prompt + msg.substring(11, msg.length)
val context = prompt.split(",").toMutableList()
// prompt 设置为5,表示缓存的上下文长度为5条消息
if (context.size < 5) {
context.add(msg.substring(11, msg.length))
} else {
context.removeAt(0)
context.add(msg.substring(11, msg.length))
}
val OPENAPI = "https://api.openai.com/v1/completions"
val headers = Headers.headersOf("Authorization","Bearer sk-xxxxxxxx","Content-Type","application/json;charset=utf-8")
val localhostProxy: Proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress("127.0.0.1", 8080))
var chatInfo = JSONObject.of("model", "text-davinci-003", "prompt", chatMsg,"max_tokens",500)
println(chatInfo.toString())
val request = Request.Builder().url(OPENAPI).headers(headers).post(chatInfo.toString().toRequestBody("application/json;charset=utf-8".toMediaType())).build()
var client = OkHttpClient.Builder()//.proxy(localhostProxy) //设置代理
.connectTimeout(60L, TimeUnit.SECONDS) //连接超时
.readTimeout(60L, TimeUnit.SECONDS) //读取超时
.writeTimeout(60L, TimeUnit.SECONDS) //写超时
.build()
try {
val response = client.newCall(request).execute()
var content = JSONObject.parseObject(response.body!!.string()).getJSONArray("choices").getJSONObject(0).getString("text").toString()
this.group.sendMessage(message.quote() + At(sender.id) + content.replace("/n"," ") )
//LangBaoServer.updateMoney(sender.id,-1,sender.id)
// prompt 设置为5,表示缓存的上下文长度为5条消息
if (context.size < 5) {
context.add(content)
} else {
context.removeAt(0)
context.add(content)
}
if(response.body != null) {
response.body!!.close();
}
}catch (e: IOException){
println(e.message)
}
//将新一轮的prompt存入数据库
jedis.set(group.id.toString()+"-"+ sender.id.toString(),context.toString()
}
//清空prompt,过长的prompt会导致token的快速消耗
msg == "#update prompt" -> {
val jedis: Jedis = Jedis("...",6379)
jedis.auth("Xxxx")
jedis.set(group.id.toString()+"-"+ sender.id.toString(),"[-]")
}