尝试使用Qt调用OpenAI API

2023年6月26日 0 By Majjcom

目前经过浅浅的尝试,已经可以成功调用OpenAI的API了。

验证

首先是通过Python进行测试:

OpenAI的API通过HTTP进行调用,所以可以使用Python的request库来操作。
当然,用requset库还有一个原因是它可以很方便地做ChatGPT的流式输出

简单的代码如下:

# 准备API以及模型信息
url = 'https://api.openai.com/v1/chat/completions'

headers = {
    "Content-type": "application/json",
    "Authorization": "Bearer sk-******"
}

data_base = {
    "model": "gpt-3.5-turbo",
    "temperature": 0.7,
    "top_p": 1,
    "n": 1,
    "max_tokens": 512,
    "presence_penalty": 0,
    "frequency_penalty": 0,
    "stream": True
}

# 储存历史记录
chats = [

]

# 解析接收的流式数据
def parse_data(data: bytes):
    if data == b'\n':
        return False
    s = data.decode('utf_8')
    p = s.find(':')
    s = s[p + 2:]
    if s == '[DONE]\n':
        return True
    j = json.loads(s)
    return j['choices'][0]['delta']

# Chat主要逻辑
while True:
    user = input("User: ")
    if len(user) != 0:
        chats.append({
            'role': 'user',
            'content': user
        })
    send = copy.deepcopy(data_base)
    send['messages'] = chats
    send_json = json.dumps(send)
    req = urllib.request.Request(url, send_json.encode('utf_8'), headers)
    print("Assistant: ", end='')
    ass = {
        'role': '',
        'content': ''
    }
    # 处理流式数据
    with urllib.request.urlopen(req) as resp:
        for line in resp:
            # print('line:', line)
            get = parse_data(line)
            if get != False and get != True:
                for i in get:
                    ass[i] = ass.get(i, '') + get[i]
                    if i == 'content':
                        print(get[i], end='')
            elif get == True:
                print()
    chats.append(ass)

于是就用Python简单实现了该功能。

实践

当然,在这里实现和在Qt中的实现还是有很大差距的。

在Qt中,我们需要使用QNetworkAccessManager QNetworkRequest QNetworkReply这几个类来实现流的功能。
在Qt中,创建一个Request,然后让manager发送request,会返回一个Reply对象,通过连接信号readyRead可以处理所有发送来的数据,这样就可以实现流式输出了。

在对话显示上,我用了别人写的MarkDown高亮显示(QMarkdownTextEdit),甚至支持很多代码的高亮显示,刚好能满足要求。

最后还是做出了一点好东西

尾声

不过Qt代码还是存在问题

如果接收到的消息是不完整的,这一轮对话会直接报错,但事实上在正常运行,这会消耗我的API调用次数,增加开销。而且没有做自动token计数和token裁剪,导致使用体验不佳。

不过,好歹还是可以用的。后来又给它加了一个可以修改服务器地址的功能,这样就可以直接调用国内的代理了。

这个项目大概就这样了吧。

——Majjcom