Python调用智谱API实现简单AI对话框

《智谱 AI 聊天应用的开发与实现》
在当今数字化时代,即时通讯和智能交互技术的发展日新月异。本文将详细介绍一个基于 Python 的智能 AI2.0 聊天应用的代码实现,该应用包含客户端和服务器端,为用户提供了一个便捷的聊天交流平台,并融入了智能 AI 助手的功能。

一、应用概述

这个智能 AI2.0 聊天应用旨在实现用户之间的实时聊天,并借助 AI 技术提供智能回复和交互。用户可以通过客户端加入和离开聊天室,发送和接收消息,同时服务器端负责管理客户端连接、转发消息以及与 AI 助手进行交互。

二、客户端代码解析

(一)界面设计与初始化

窗口设置
使用wxPython库创建一个名为 “智能 AI2.0 聊天客户端” 的窗口,设置窗口大小为(480, 700)。
通过设置窗口和面板的背景色,营造出舒适的视觉效果。整体背景色为更淡的灰色(wx.Colour(245, 245, 245)),面板背景色为白色(wx.Colour(255, 255, 255))。
按钮创建
创建了多个按钮,包括 “加入聊天室”(淡绿色按钮wx.Colour(144, 238, 144))、“离开聊天室”(淡粉色按钮wx.Colour(255, 192, 203))、“清空”(淡黄色按钮wx.Colour(255, 255, 153))和 “发送”(淡蓝色按钮wx.Colour(173, 216, 230))按钮。每个按钮都设置了相应的字体和颜色,使其在界面上清晰可辨且美观。
文本框设置
有两个文本框,一个是聊天记录文本框self.chat_text,用于显示聊天记录,设置为多行只读模式(wx.TE_MULTILINE | wx.TE_READONLY)。另一个是输入文本框self.input_text,用于用户输入消息,设置为多行模式(wx.TE_MULTILINE)。

(二)事件绑定

按钮事件
分别为每个按钮绑定了相应的事件处理函数。例如,点击 “加入聊天室” 按钮会调用join_chat函数,点击 “离开聊天室” 按钮会调用leave_chat函数等。
回车键事件
为输入文本框绑定了回车键事件wx.EVT_KEY_DOWN。当用户按下回车键时,如果没有同时按下 Ctrl 键,则调用send_message函数发送消息;如果同时按下 Ctrl 键,则在输入文本框中换行。

(三)聊天功能实现

加入聊天室
在join_chat函数中,当用户点击 “加入聊天室” 按钮且未连接时,尝试连接到服务器(127.0.0.1:8999)。首先将用户的随机生成的名字(通过Faker库生成)发送给服务器进行注册,然后启动一个新线程receive_data来接收服务器端发送的数据,并在连接成功后显示提示信息。如果连接失败,则弹出错误提示框。
接收数据
receive_data函数在一个独立的线程中运行,不断接收服务器发送的数据。接收到数据后,根据数据的编码格式(可能是utf-8或GBK)进行解码。如果消息以 “【系统消息】” 或 “【AI2.0 助手】” 开头,则直接将消息添加到聊天记录文本框;如果不是,则以 “【AI 小秘】” 开头添加到聊天记录文本框。
离开聊天室
leave_chat函数在用户点击 “离开聊天室” 按钮时被调用。如果已连接,向服务器发送 “断开连接” 消息,然后设置连接状态为False,并显示离开聊天室的提示信息。如果发送断开连接消息失败,则弹出错误提示框。
清空聊天记录
clear_chat函数在用户点击 “清空” 按钮时,清空聊天记录文本框的内容。
发送消息
send_message函数在用户点击 “发送” 按钮或按下回车键(未按下 Ctrl 键)时被调用。如果已连接,将用户输入的消息发送给服务器,然后将消息以 “【我】” 开头添加到聊天记录文本框,并清空输入文本框。如果发送消息失败,则弹出错误提示框。

三、服务器端代码解析

(一)界面设计与初始化

窗口设置
创建一个名为 “智能 AI2.0 聊天服务器” 的窗口,设置窗口大小为(500, 650),并设置整体和面板的背景色与客户端类似,以保持风格一致。
按钮创建
有 “启动服务器”(淡绿色按钮)和 “保存聊天记录”(淡蓝色按钮)两个按钮。“启动服务器” 按钮用于启动服务器监听客户端连接,“保存聊天记录” 按钮用于将聊天记录保存到文件中。
文本框设置
聊天记录文本框self.chat_text用于显示服务器端的聊天记录,设置为多行只读模式。

(二)事件绑定

为 “启动服务器” 按钮绑定start_server函数,为 “保存聊天记录” 按钮绑定save_chat_record函数。

(三)服务器功能实现

启动服务器
在start_server函数中,当点击 “启动服务器” 按钮且服务器未运行时,尝试绑定服务器端口(0.0.0.0:8999)并开始监听。启动一个新线程server_loop来处理客户端连接请求。如果启动成功,显示提示信息;如果失败,则弹出错误提示框。
服务器循环
server_loop函数在一个独立的线程中运行,不断接受客户端连接。当有客户端连接时,接收客户端发送的用户名,创建一个ClientHandler线程来处理该客户端的通信,并将客户端线程添加到client_threads字典中进行管理。同时,向所有客户端发送欢迎消息,并调用send_message函数将消息发送给 AI 助手获取回复并转发给客户端。
发送消息
send_message函数将消息添加到服务器端的聊天记录文本框,并通过ThreadPoolExecutor并发地向所有在线客户端发送消息。同时,将用户发送的消息传递给 AI 助手获取回复,并将回复发送给客户端,同时将 AI 助手的回复也添加到聊天记录文本框中。
保存聊天记录
save_chat_record函数在用户点击 “保存聊天记录” 按钮时被调用。它获取聊天记录文本框的内容,将其写入到record.log文件中。如果保存成功,显示提示信息;如果失败,则弹出错误提示框。

(四)客户端处理线程

客户端处理器类
ClientHandler类继承自threading.Thread,用于处理单个客户端的通信。在初始化时,接收客户端套接字、用户名和服务器实例。
运行方法
run方法在一个独立的线程中运行,不断接收客户端发送的消息。如果接收到的消息是 “断开连接”,则设置客户端线程的运行状态为False,并向其他客户端发送该用户离开的消息。如果接收到其他消息,则将消息转发给服务器的send_message函数进行处理。当出现接收消息错误时,设置运行状态为False。最后,尝试关闭客户端套接字。

代码展示:

from zhipuai import ZhipuAI

class ZhipuAIChat:
    def __init__(self, api_key):
        self.name = "AI小秘"
        self.client = ZhipuAI(api_key=api_key)

    def get_response(self, question):
        messages = [
            {
    
    "role": "user", "content": question}
        ]
        response = self.client.chat.completions.create(
            model="glm-4-flash",
            messages=messages
        )
        try:
            return response.choices[0].message.content
        except AttributeError:
            return "无法获取回答"


这里我是把调用API接口封装成了一个类一别创建对象。

import wx
from socket import *
import threading
from concurrent.futures import ThreadPoolExecutor
import ai_test

# 创建 AI2.0 聊天实例
ai_chat = ai_test.ZhipuAIChat('*************************')#这里填写自己的key

class ServerApp(wx.Frame):
    def __init__(self):
        # 设置窗口标题为“智能 AI2.0 聊天服务器”
        super().__init__(None, title='智能 AI2.0 聊天服务器', size=(500, 650))
        self.is_running = False
        self.server_socket = socket()
        self.server_socket.bind(('0.0.0.0', 8999))
        self.server_socket.listen(5)
        self.client_threads = {
    
    }
        self.pool = ThreadPoolExecutor(max_workers=10)

        self.SetBackgroundColour(wx.Colour(245, 245, 245))  # 整体背景色设置为更淡的灰色

        # 创建面板并设置布局管理器
        self.panel = wx.Panel(self)
        self.panel.SetBackgroundColour(wx.Colour(255, 255, 255))  # 面板背景色设置为白色

        sizer = wx.BoxSizer(wx.VERTICAL)

        # 创建启动服务器按钮
        self.start_server_btn = wx.Button(self.panel, label='启动服务器')
        self.start_server_btn.SetBackgroundColour(wx.Colour(144, 238, 144))  # 淡绿色按钮
        self.start_server_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.start_server_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.start_server_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建保存聊天记录按钮
        self.save_text_btn = wx.Button(self.panel, label='保存聊天记录')
        self.save_text_btn.SetBackgroundColour(wx.Colour(173, 216, 230))  # 淡蓝色按钮
        self.save_text_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.save_text_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.save_text_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建聊天记录文本框
        self.chat_text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY)
        sizer.Add(self.chat_text, 1, wx.EXPAND | wx.ALL, 5)

        self.panel.SetSizer(sizer)

        # 绑定按钮事件
        self.Bind(wx.EVT_BUTTON, self.start_server, self.start_server_btn)
        self.Bind(wx.EVT_BUTTON, self.save_chat_record, self.save_text_btn)

    def start_server(self, event):
        if not self.is_running:
            try:
                self.is_running = True
                threading.Thread(target=self.server_loop, daemon=True).start()
                wx.MessageBox('服务器已启动!', '提示', wx.OK | wx.ICON_INFORMATION)
            except Exception as e:
                wx.MessageBox(f'启动服务器失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def server_loop(self):
        while self.is_running:
            try:
                client_socket, client_addr = self.server_socket.accept()
                client_name = client_socket.recv(1024).decode('utf-8')
                client_thread = ClientHandler(client_socket, client_name, self)
                self.client_threads[client_name] = client_thread
                self.pool.submit(client_thread.run)
                self.send_message(f'【系统消息】欢迎 {
      
      client_name} 进入聊天室。')
            except Exception as e:
                wx.MessageBox(f'接受客户端连接错误:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def send_message(self, text):
        self.chat_text.AppendText(text + '\n')
        for client in self.client_threads.values():
            if client.is_running:
                try:
                    result = ai_chat.get_response(text)
                    client.client_socket.send(result.encode('utf-8'))
                    self.chat_text.AppendText(f'【{
      
      ai_chat.name}】:{
      
      result}\n')
                except Exception as e:
                    wx.MessageBox(f'发送消息给客户端失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def save_chat_record(self, event):
        try:
            record = self.chat_text.GetValue()
            with open('record.log', 'a+', encoding='utf-8') as f:
                f.write(record)
            wx.MessageBox('聊天记录已保存!', '提示', wx.OK | wx.ICON_INFORMATION)
        except Exception as e:
            wx.MessageBox(f'保存聊天记录失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

class ClientHandler(threading.Thread):
    def __init__(self, socket, name, server):
        threading.Thread.__init__(self)
        self.client_socket = socket
        self.client_name = name
        self.server = server
        self.is_running = True

    def run(self):
        while self.is_running:
            try:
                received_data = self.client_socket.recv(1024)
                encoding = 'utf-8'
                try:
                    text = received_data.decode(encoding)
                except UnicodeDecodeError:
                    encoding = 'GBK'
                    text = received_data.decode(encoding)
                if text == '断开连接':
                    self.is_running = False
                    self.server.send_message(f'【系统消息】{
      
      self.client_name} 已离开聊天室。')
                else:
                    self.server.send_message(f'【{
      
      self.client_name}】:{
      
      text}')
            except Exception as e:
                wx.MessageBox(f'接收客户端消息错误:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)
                self.is_running = False
        try:
            self.client_socket.close()
        except Exception as e:
            wx.MessageBox(f'关闭客户端连接失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

if __name__ == '__main__':
    app = wx.App()
    server = ServerApp()
    server.Show()
    app.MainLoop()

服务端这里我是也做了一个组件界面。

import wx
from socket import *
import threading
from faker import Faker

class ClientApp(wx.Frame):
    def __init__(self):
        # 设置窗口标题为“智能 AI2.0 聊天客户端”
        super().__init__(None, title='智能 AI2.0 聊天客户端', size=(480, 700))
        self.name = Faker('zh_CN').name()
        self.connected = False
        self.client_socket = None

        self.SetBackgroundColour(wx.Colour(245, 245, 245))  # 整体背景色设置为更淡的灰色

        # 创建面板并设置布局管理器
        self.panel = wx.Panel(self)
        self.panel.SetBackgroundColour(wx.Colour(255, 255, 255))  # 面板背景色设置为白色

        sizer = wx.BoxSizer(wx.VERTICAL)

        # 创建加入聊天室按钮
        self.join_btn = wx.Button(self.panel, label='加入聊天室')
        self.join_btn.SetBackgroundColour(wx.Colour(144, 238, 144))  # 淡绿色按钮
        self.join_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.join_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.join_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建离开聊天室按钮
        self.leave_btn = wx.Button(self.panel, label='离开聊天室')
        self.leave_btn.SetBackgroundColour(wx.Colour(255, 192, 203))  # 淡粉色按钮
        self.leave_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.leave_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.leave_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建清空按钮
        self.clear_btn = wx.Button(self.panel, label='清空')
        self.clear_btn.SetBackgroundColour(wx.Colour(255, 255, 153))  # 淡黄色按钮
        self.clear_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.clear_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.clear_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建发送按钮
        self.send_btn = wx.Button(self.panel, label='发送')
        self.send_btn.SetBackgroundColour(wx.Colour(173, 216, 230))  # 淡蓝色按钮
        self.send_btn.SetForegroundColour(wx.Colour(0, 0, 0))
        self.send_btn.SetFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
        sizer.Add(self.send_btn, 0, wx.EXPAND | wx.ALL, 5)

        # 创建聊天记录文本框
        self.chat_text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE | wx.TE_READONLY)
        sizer.Add(self.chat_text, 1, wx.EXPAND | wx.ALL, 5)

        # 创建输入文本框
        self.input_text = wx.TextCtrl(self.panel, style=wx.TE_MULTILINE)
        sizer.Add(self.input_text, 0, wx.EXPAND | wx.ALL, 5)

        self.panel.SetSizer(sizer)

        # 绑定按钮事件
        self.Bind(wx.EVT_BUTTON, self.join_chat, self.join_btn)
        self.Bind(wx.EVT_BUTTON, self.leave_chat, self.leave_btn)
        self.Bind(wx.EVT_BUTTON, self.clear_chat, self.clear_btn)
        self.Bind(wx.EVT_BUTTON, self.send_message, self.send_btn)
        # 绑定回车键事件
        self.input_text.Bind(wx.EVT_KEY_DOWN, self.on_key_down)

    def on_key_down(self, event):
        key_code = event.GetKeyCode()
        if key_code == wx.WXK_RETURN and not event.ControlDown():
            self.send_message(None)
        elif key_code == wx.WXK_RETURN and event.ControlDown():
            self.input_text.AppendText('\n')
        else:
            event.Skip()

    def join_chat(self, event):
        if not self.connected:
            try:
                self.connected = True
                self.client_socket = socket()
                self.client_socket.connect(('127.0.0.1', 8999))
                self.client_socket.send(self.name.encode('utf-8'))
                threading.Thread(target=self.receive_data, daemon=True).start()
                wx.MessageBox('已加入聊天室!', '提示', wx.OK | wx.ICON_INFORMATION)
            except Exception as e:
                wx.MessageBox(f'连接失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def receive_data(self):
        while self.connected:
            try:
                received_data = self.client_socket.recv(1024)
                encoding = 'utf-8'
                try:
                    text = received_data.decode(encoding)
                except UnicodeDecodeError:
                    encoding = 'GBK'
                    text = received_data.decode(encoding)
                if text.startswith('【系统消息】'):
                    self.chat_text.AppendText(text + '\n')
                else:
                    if text.startswith('【AI2.0 助手】'):
                        self.chat_text.AppendText(text + '\n')
                    else:
                        self.chat_text.AppendText(f'【AI小秘】{
      
      text}\n')
            except Exception as e:
                wx.MessageBox(f'接收数据错误:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)
                self.connected = False

    def leave_chat(self, event):
        if self.connected:
            try:
                self.client_socket.send('断开连接'.encode('utf-8'))
                self.connected = False
                wx.MessageBox('已离开聊天室!', '提示', wx.OK | wx.ICON_INFORMATION)
            except Exception as e:
                wx.MessageBox(f'发送断开连接消息失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

    def clear_chat(self, event):
        self.chat_text.Clear()

    def send_message(self, event):
        if self.connected:
            text = self.input_text.GetValue()
            if text:
                try:
                    self.client_socket.send(text.encode('utf-8'))
                    self.chat_text.AppendText(f'【我】{
      
      text}\n')
                    self.input_text.Clear()
                except Exception as e:
                    wx.MessageBox(f'发送消息失败:{
      
      str(e)}', '错误', wx.OK | wx.ICON_ERROR)

if __name__ == '__main__':
    app = wx.App()
    client = ClientApp()
    client.Show()
    app.MainLoop()

服务端主要就是用户提问的地方了。

四、总结

通过以上代码实现,成功构建了一个智能 AI2.0 聊天应用。客户端提供了友好的用户界面,方便用户进行聊天操作和与服务器交互。服务器端则负责管理客户端连接、消息转发以及与 AI 助手的交互,实现了智能聊天的核心功能。这个应用不仅展示了 Python 在网络编程和图形界面开发方面的强大能力,还融入了 AI 技术,为用户提供了更加智能和便捷的聊天体验。未来,可以进一步优化代码,增加更多的功能,如用户认证、表情支持等,以提升应用的实用性和趣味性。

猜你喜欢

转载自blog.csdn.net/qq_68076599/article/details/143243079