利用Openai_api制作聊天机器人

发布于 2023-08-19  304 次阅读


利用Openai_api制作聊天机器人

项目已开源,开源地址:https://github.com/MYJOKERML/chatBot

openai_api获取地址:https://platform.openai.com/account/api-keys,需要自己有一个OPENAI账号,注意OPENAI的API免费额度有时间限制,用不了大概率是免费额度过期了

一、设置OPENAI_API_KEY环境变量

参考链接:https://sysin.org/blog/windows-env/

为了方便,直接使用管理员权限打开powershell,一行命令解决

[environment]::SetEnvironmentvariable("OPENAI_API_KEY", "Your_api_key", "Machine")

输入命令查看环境变量

[System.Environment]::GetEnvironmentVariable("OPENAI_API_KEY", "Machine")

二、编写聊天机器人代码

根据上述步骤设置好环境变量后可以在代码中验证设置成功与否

import os
import openai

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
print(OPENAI_API_KEY)

聊天的代码很好写,都是现成的。

# Execute api's function
def get_completion(prompt, model="gpt-3.5-turbo", temperature=0):
    messages = [{"role": "user", "content": prompt}]
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature,
    )
    return response.choices[0].message["content"]

def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
    response = openai.ChatCompletion.create(
        model=model,
        messages=messages,
        temperature=temperature,
    )
    return response.choices[0].message["content"]

根据现成的代码我写了一个类用于实现上下文的对话

from textwrap import dedent
test_role = ('你是一个' + \
                '学富五车的哲学家,深贯中西哲学思想,' + \
                '而且你对于生与死有着极其深刻的理解,' + \
                '你也一直在探寻生命存在的意义。现在有一名同样也在探寻生命意义的学生找到了你' + \
                '希望能与你探讨生与死的意义,同时也探寻人存在的意义'
                )
class ChatContent:
    def __init__(self, custom_role="") -> None:
        self.content = [{'role':'system', 'content':f"{custom_role}"},]

    def add(self, role, content):
        self.content.append({'role':role, 'content':content})

    def get(self):
        return self.content

    def get_completion_from_messages(self, model="gpt-3.5-turbo", temperature=0):
        response = openai.ChatCompletion.create(
            model=model,
            messages=self.content,
            temperature=temperature,
        )
        self.add('assistant', response.choices[0].message["content"])
        return response.choices[0].message["content"]

    def chat(self, input_content, model="gpt-3.5-turbo", temperature=0):
        self.add('user', input_content)
        return self.get_completion_from_messages(model, temperature)

然后我通过提示词工程让chatGPT为我写了份GUI界面

提示词如下:

prompt = """
    你的任务是使用python为一个聊天机器人写一个GUI界面。要求有:
    1. 一个输入框,用于用户输入内容
    2. 一个按钮,用于用户点击,触发机器人回复
    3. 一个文本框,用于显示机器人回复的内容
    4. 机器人回复的内容需要有多行,需要能够自动换行
    5. 以对话的形式显示聊天记录
    6. 代码中清晰地注释我该在什么地方将用户输入的内容传给机器人,以及将机器人的回复如何显示在文本框中
    7. 有一个按钮,点击后可以清空聊天记录
    8. 有一个按钮,点击后可以保存聊天记录到本地,以时间命名文件
    9. 有一个按钮,点击后可以读取本地聊天记录,显示在文本框中
    10. 使用utf8编码保存和读取聊天记录
"""

response = get_completion(prompt)

当然没法一次性生成质量很好的代码,但写得算是一段比较完整的代码了,再通过不停地与GPT交流完善代码,对比初始生成代码主要改了几个地方:

  1. 添加了一个初始化自定义催眠词的窗口,(还缺一个未输入默认为空没写
  2. 修改了文本框大小布局,使其自适窗口
  3. 显示正在响应的设计
  4. 新开一个线程以解决生成回复时窗口卡顿的问题
  5. 修改窗口字体

最后修改代码如下:

import tkinter as tk
from tkinter import filedialog, simpledialog
from datetime import datetime
import threading

class ChatBotGUI:
    def __init__(self, custom_hypnosis=''):

        self.custom_hypnosis = custom_hypnosis

        self.init_window = tk.Tk()
        self.init_window.title("Initialize")

        # 创建文本框,用于输入初始化参数custom_hypnosis
        self.init_text = tk.Text(self.init_window, wrap=tk.WORD, height=3, font=("微软雅黑", 12))
        self.init_text.insert("1.0", self.custom_hypnosis)
        self.init_text.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        self.init_text.bind("<KeyRelease>", self.adjust_init_height)

        # 创建按钮,用于确认初始化参数custom_hypnosis
        self.confirm_button = tk.Button(self.init_window, text="Confirm", command=self.confirm_init)
        self.confirm_button.grid(row=0, column=1, pady=5)

        # 设置网格布局的行和列权重,使文本框能够自适应放大
        self.init_window.grid_rowconfigure(1, weight=1)
        self.init_window.grid_columnconfigure(0, weight=1)

        self.init_window.mainloop()

        self.window = tk.Tk()
        self.window.title("Chat Bot")

        # 创建文本框,用于输入用户内容
        self.input_text = tk.Text(self.window, wrap=tk.WORD, height=3, font=("微软雅黑", 12))
        self.input_text.grid(row=0, column=0, padx=10, pady=10, sticky="ew")
        self.input_text.bind("<KeyRelease>", self.adjust_input_height)

        # 创建按钮,用于触发机器人回复
        self.reply_button = tk.Button(self.window, text="Send", command=self.get_reply)
        self.reply_button.grid(row=0, column=1, pady=5)

        # 创建文本框,用于显示机器人回复的内容
        self.text_box = tk.Text(self.window, width=50, height=10, wrap=tk.WORD, font=("Arial", 12))
        self.text_box.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")

        # 创建按钮,用于清空聊天记录
        self.clear_button = tk.Button(self.window, text="Clear", command=self.clear_chat)
        self.clear_button.grid(row=2, column=0, pady=5)

        # 创建按钮,用于保存聊天记录到本地
        self.save_button = tk.Button(self.window, text="Save", command=self.save_chat)
        self.save_button.grid(row=2, column=1, pady=5)

        # 创建按钮,用于读取本地聊天记录
        self.load_button = tk.Button(self.window, text="Load", command=self.load_chat)
        self.load_button.grid(row=2, column=2, pady=5)

        # 设置网格布局的行和列权重,使文本框能够自适应放大
        self.window.grid_rowconfigure(1, weight=1)
        self.window.grid_columnconfigure(0, weight=1)

        self.window.mainloop()

    def adjust_init_height(self, event=None):
        lines = self.init_text.get("1.0", tk.END).count("\n") + 1
        self.init_text.config(height=lines)
        self.init_window.update_idletasks()

    def confirm_init(self):
        self.custom_hypnosis = self.init_text.get("1.0", tk.END).strip()
        self.init_text.config(state=tk.DISABLED)
        self.confirm_button.config(state=tk.DISABLED)

        # 创建ChatContent对象,用于获取机器人的回复
        self.chat_content = ChatContent(self.custom_hypnosis)

        self.init_window.destroy()  # 关闭初始化窗口

    def adjust_input_height(self, event=None):
        lines = self.input_text.get("1.0", tk.END).count("\n") + 1
        self.input_text.config(height=lines)
        self.window.update_idletasks()

    def get_reply(self):
       # 获取用户输入的内容
        user_input = self.input_text.get("1.0", tk.END).strip()  # 获取输入文本的全部内容

        # 在文本框中显示用户输入
        self.text_box.insert(tk.END, "User: " + user_input + "\n")
        self.text_box.see(tk.END)  # 滚动文本框以显示新的内容
        self.window.update_idletasks()

        # 在文本框中显示等待提示
        self.text_box.insert(tk.END, "Bot: (Replying...)\n")
        self.text_box.see(tk.END)  # 滚动文本框以显示新的内容
        self.text_box.update_idletasks()  # 刷新界面,以便立即显示等待提示

        # 为了防止卡顿,创建一个线程,用于获取机器人的回复
        thread = threading.Thread(target=self.get_and_display_bot_reply, args=(user_input,))
        thread.start()

        # 清空输入框
        self.input_text.delete("1.0", tk.END)

    def get_and_display_bot_reply(self, user_input):
        # 将用户输入的内容传给机器人,获取机器人的回复
        bot_reply = self.get_bot_reply(user_input)

        # 在文本框中显示机器人回复
        self.text_box.delete("end - 2 lines", tk.END)  # 清除之前的等待提示和机器人回复
        self.text_box.insert(tk.END, "\nBot: " + bot_reply + "\n\n")
        self.text_box.see(tk.END)  # 滚动文本框以显示新的内容

    def get_bot_reply(self, user_input):
        # 在这里编写机器人的回复逻辑
        response = self.chat_content.chat(user_input)
        # 返回机器人的回复
        return response

    def clear_chat(self):
        # 清空文本框
        self.text_box.delete(1.0, tk.END)

    def save_chat(self):
        # 获取当前时间
        current_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")

        # 生成文件名
        file_name = current_time + ".txt"

        # 打开文件并写入聊天记录
        with open(file_name, "w", encoding="utf-8") as file:
            file.write(self.text_box.get(1.0, tk.END))

    def load_chat(self):
        # 打开文件对话框并读取聊天记录
        file_name = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
        if file_name:
            with open(file_name, "r", encoding="utf-8") as file:
                chat_content = file.read()

            # 将聊天记录显示在文本框中
            self.text_box.delete(1.0, tk.END)
            self.text_box.insert(tk.END, chat_content)

# 创建ChatBotGUI对象
chat_bot_gui = ChatBotGUI(test_role)

后续计划增加的功能:

  • load对话不仅仅单纯显示对话,还要将对话载入为上下文继续进行内容的生成,可以考虑使用二进制储存以节约空间
  • 增加撤回最近一条消息的功能
  • 增加设置temperature的功能
  • save成功和load成功的提示

自己写聊天机器人的优缺点:

优点:

  • 增强代码能力,为prompt工程积累经验
  • 使用起来方便,比如偶尔只是想问个小问题,网页版就要新开一个chat,代码的话只需要修改个变量就行,很方便,而且修改custom instruction也只是改变量名,会快很多

缺点:

  • 很明显,我写这么多能做的事,网页版chatGPT也全都能做,还做得更好
  • 生成速度奇慢无比,比网页版慢好多好多倍,像我之前那个GUI的代码,用API生成用了几分钟,网页版秒生成

thoughts:

其实写这么多代码能做的也完全比不上网页版的cahtGPT,那我做这么多不是傻吗?其实并不是,写这个代码很大程度也是为熟练使用prompt工程做学习铺垫,而且我一直认为这种直接在代码层面的交互方式会比用鼠标点来点去的交互方式有效率得多。而且写这个东西相当于就是在做一个产品了,在制作过程中,我会发现各种各样奇怪的用起来很不爽的bug,比如字体大小,文本框怎么固定不变之类的,在LLM出来以前我只要去网上慢慢查,效率极低,而现在,在我完全不懂GUI的情况下,一晚上在chatGPT的帮助下就做出来一个产品的简单雏形,内心的成就感不由得是极高的。像随时想加个什么新功能,解决卡顿问题之类的,问chatGPT真的很快就做出来了,也为如何更好地和LLM沟通积累经验。后续我也希望能继续完善这个作品,虽然就只是利用api复现了chatGPT的一些功能,但这也是有意义的,因为后续我希望能利用这样的能力去实现一些有意思的prompt工程。

image-20230819234656109
image-20230819234726026
image-20230819235131690


2023-8-20

继续更新,将框架全部更换为了PyQT5,用起来确实会舒服一丢丢,然后又不停地该细节,虽然仍然还有很多不完善的地方,但对比昨天来看又丰富了不少细节。首先就是速度,最开始用的python自带的threading,会和PyQT5出现冲突,所以在chatGPT的帮助下换成了QThread,换了之后发现速度竟然比之前快诶,有点小意外,也不枉我加这么多代码。

然后就对clear功能进行完善,清空了对话,Save储存格式更改为了json格式,Load不再仅仅是显示对话,也能够加载对话,继续加载对话的聊天内容。同时添加了更改字体的功能,能够手动更改字体格式大小。做到这里我觉得也就差不多能够将就使用了,本来也就是个人日常偶尔想问一个简单问题时的小工具,这样已经差不多够自己使用了。

目前是所有按钮功能都实现了,对话框还有瑕疵,但不想改了。后面要继续学算法去了,争取后面部署上自己训练的LLM

image-20230820162629821


整天不想事儿,就想着干饭