Python实现AI聊天窗口的两种方案
·
在Python中调用API创建AI聊天窗口,核心是将图形用户界面(GUI)与AI模型的HTTP API调用相结合,实现用户输入、API请求、响应展示的完整闭环。以下是两种主流、可立即运行的实现方案,涵盖从环境搭建到代码部署的全过程。
一、 方案对比与选型指南
在开始编码前,可根据下表选择最适合你需求的技术栈:
| 特性维度 | 方案一:Tkinter + 通用API | 方案二:PySimpleGUI + 官方SDK | 方案三:本地模型 + 轻量GUI |
|---|---|---|---|
| GUI库 | Tkinter (Python内置) | PySimpleGUI (基于Tkinter封装) | 任选 (Tkinter/PySimpleGUI) |
| API调用方式 | requests 库发送HTTP请求 |
使用模型提供的官方Python SDK | 调用本地部署模型的API (如Ollama) |
| 优点 | 无需额外安装GUI库,控制粒度细,学习资源丰富 | 代码更简洁,布局更直观,开发速度快 | 完全离线,数据隐私性强,无网络延迟 |
| 缺点 | 原生API较底层,复杂布局代码量多 | 需额外安装PySimpleGUI包 |
需本地部署模型,消耗计算资源 |
| 适用场景 | 希望深入理解底层机制的教学或原型项目 | 追求开发效率的快速应用搭建 | 对数据隐私有严格要求或网络不便的环境 |
二、 方案一详解:使用Tkinter与requests调用智谱AI API
此方案使用Python标准库,适合希望掌握每个步骤细节的开发者。
1. 环境准备与依赖安装
# 安装HTTP请求库
pip install requests
关键步骤:前往智谱AI开放平台注册并创建应用,获取你的API Key。
2. 完整可运行代码
创建一个名为ai_chat_tkinter.py的文件,粘贴以下代码并替换your_api_key_here为你的真实密钥。
import tkinter as tk
from tkinter import scrolledtext, messagebox, END
import requests
import json
import threading
from datetime import datetime
class AIChatApp:
def __init__(self, root):
self.root = root
self.root.title("智谱AI聊天助手 - Tkinter版")
self.root.geometry("750x850")
# ========== 1. API配置 ==========
self.api_key = "your_api_key_here" # 请在此处替换为你的API Key
self.api_url = "https://open.bigmodel.cn/api/paas/v4/chat/completions"
self.model = "glm-4-flash" # 可选模型: glm-4, glm-4-plus, glm-3-turbo
self.max_tokens = 1024
# ========== 2. 初始化对话历史 ==========
self.conversation_history = [
{"role": "system", "content": "你是一个专业、友好且乐于助人的AI助手。"}
]
# ========== 3. 创建GUI界面组件 ==========
self.setup_ui()
def setup_ui(self):
"""构建用户界面"""
# 主框架:采用网格布局
main_frame = tk.Frame(self.root, padx=15, pady=15)
main_frame.pack(fill=tk.BOTH, expand=True)
# 3.1 标题标签
title_label = tk.Label(
main_frame,
text="💬 AI 对话助手",
font=("微软雅黑", 16, "bold"),
fg="#2c3e50"
)
title_label.grid(row=0, column=0, columnspan=2, pady=(0, 15))
# 3.2 聊天历史显示区域(带滚动条)
history_frame = tk.LabelFrame(main_frame, text="对话历史", padx=10, pady=10)
history_frame.grid(row=1, column=0, columnspan=2, sticky="nsew", pady=(0, 15))
# 配置网格权重,使聊天区域可伸缩
main_frame.grid_rowconfigure(1, weight=1)
main_frame.grid_columnconfigure(0, weight=1)
main_frame.grid_columnconfigure(1, weight=1)
self.chat_display = scrolledtext.ScrolledText(
history_frame,
wrap=tk.WORD,
width=80,
height=25,
font=("宋体", 11),
state='normal'
)
self.chat_display.pack(fill=tk.BOTH, expand=True)
self.chat_display.tag_config("user", foreground="blue", font=("宋体", 11, "bold"))
self.chat_display.tag_config("ai", foreground="green", font=("宋体", 11))
self.chat_display.tag_config("error", foreground="red")
# 3.3 输入控件框架
input_frame = tk.Frame(main_frame)
input_frame.grid(row=2, column=0, columnspan=2, sticky="ew", pady=(10, 0))
# 输入提示
input_label = tk.Label(input_frame, text="请输入您的问题:")
input_label.pack(anchor="w", pady=(0, 5))
# 多行输入文本框
self.input_text = scrolledtext.ScrolledText(
input_frame,
height=4,
wrap=tk.WORD,
font=("宋体", 11)
)
self.input_text.pack(fill=tk.X, pady=(0, 10))
self.input_text.bind("<Control-Return>", self.on_ctrl_enter) # 绑定Ctrl+Enter快捷发送
# 3.4 按钮框架
button_frame = tk.Frame(input_frame)
button_frame.pack(fill=tk.X)
send_btn = tk.Button(
button_frame,
text="发送 (Ctrl+Enter)",
command=self.send_message,
bg="#3498db",
fg="white",
font=("微软雅黑", 10),
padx=20,
pady=5
)
send_btn.pack(side=tk.LEFT, padx=(0, 10))
clear_btn = tk.Button(
button_frame,
text="清空对话",
command=self.clear_conversation,
bg="#e74c3c",
fg="white",
font=("微软雅黑", 10),
padx=20,
pady=5
)
clear_btn.pack(side=tk.LEFT)
# 3.5 状态栏
self.status_var = tk.StringVar(value="✅ 就绪")
status_bar = tk.Label(
self.root,
textvariable=self.status_var,
bd=1,
relief=tk.SUNKEN,
anchor=tk.W,
bg="#f0f0f0"
)
status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def on_ctrl_enter(self, event):
"""处理Ctrl+Enter快捷键发送"""
self.send_message()
return "break" # 阻止默认换行行为
def send_message(self):
"""处理发送消息的核心逻辑"""
# 获取并预处理用户输入
user_input = self.input_text.get("1.0", END).strip()
if not user_input:
messagebox.showwarning("输入为空", "请输入内容后再发送。")
return
# 1. 清空输入框并更新状态
self.input_text.delete("1.0", END)
self.status_var.set("⏳ AI正在思考...")
self.root.update()
# 2. 在聊天区域显示用户消息
timestamp = datetime.now().strftime("%H:%M:%S")
self.append_to_chat(f"[{timestamp}] 你:
", "user")
self.append_to_chat(f"{user_input}
")
# 3. 在新线程中调用API,避免界面冻结
thread = threading.Thread(target=self.process_ai_response, args=(user_input,))
thread.daemon = True
thread.start()
def process_ai_response(self, user_input):
"""在后台线程中处理API调用"""
try:
# 调用AI模型API
ai_response = self.call_ai_api(user_input)
# 在主线程中更新GUI
self.root.after(0, self.display_ai_response, ai_response)
except Exception as e:
error_msg = f"请求出错:{str(e)}"
self.root.after(0, self.display_error, error_msg)
def call_ai_api(self, user_message):
"""调用智谱AI ChatCompletion API"""
# 1. 准备请求头
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
# 2. 更新对话历史
self.conversation_history.append({"role": "user", "content": user_message})
# 3. 构建请求体
payload = {
"model": self.model,
"messages": self.conversation_history,
"temperature": 0.7, # 控制回复随机性 (0.0-1.0)
"max_tokens": self.max_tokens,
"stream": False
}
# 4. 发送POST请求
response = requests.post(
self.api_url,
headers=headers,
json=payload,
timeout=30 # 设置超时时间
)
response.raise_for_status() # 如果状态码不是200,抛出异常
# 5. 解析响应
result = response.json()
ai_reply = result["choices"][0]["message"]["content"]
# 6. 保存到对话历史
self.conversation_history.append({"role": "assistant", "content": ai_reply})
return ai_reply
def display_ai_response(self, response_text):
"""在主线程中安全地显示AI回复"""
timestamp = datetime.now().strftime("%H:%M:%S")
self.append_to_chat(f"[{timestamp}] AI:
", "ai")
self.append_to_chat(f"{response_text}
{'='*60}
")
self.status_var.set("✅ 就绪")
def display_error(self, error_text):
"""显示错误信息"""
self.append_to_chat(f"❌ 错误:{error_text}
", "error")
self.status_var.set("❌ 发生错误")
def append_to_chat(self, text, tag=None):
"""向聊天区域追加文本"""
self.chat_display.configure(state='normal')
if tag:
self.chat_display.insert(END, text, tag)
else:
self.chat_display.insert(END, text)
self.chat_display.configure(state='disabled')
self.chat_display.see(END) # 自动滚动到底部
def clear_conversation(self):
"""清空当前对话"""
if messagebox.askyesno("确认清空", "确定要清空对话历史吗?"):
self.chat_display.configure(state='normal')
self.chat_display.delete("1.0", END)
self.chat_display.configure(state='disabled')
# 重置对话历史,但保留系统提示
self.conversation_history = self.conversation_history[:1]
self.status_var.set("✅ 对话已清空")
# 程序入口
if __name__ == "__main__":
root = tk.Tk()
app = AIChatApp(root)
# 窗口居中显示
root.update_idletasks()
width = root.winfo_width()
height = root.winfo_height()
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
root.mainloop()
3. 代码核心解析
- 多线程处理:通过
threading.Thread将耗时的网络请求放入后台线程,确保GUI界面在等待API响应时不会卡死。 - 线程安全更新:所有对GUI组件的修改(如
append_to_chat)都通过root.after(0, ...)在主线程中执行,这是Tkinter多线程编程的最佳实践。 - 上下文管理:
self.conversation_history列表完整保存了对话的上下文,每次API调用都将其传入,使AI能理解之前的对话内容,实现真正的多轮对话。 - 错误处理:通过
try-except块捕获网络异常和API错误,并通过状态栏和颜色标签给予用户明确反馈。
三、 方案二详解:使用PySimpleGUI与DeepSeek官方SDK
此方案使用更高级的GUI库和官方SDK,代码更简洁,适合快速开发。
1. 环境准备
# 安装PySimpleGUI和DeepSeek官方SDK
pip install PySimpleGUI openai
注意:DeepSeek API与OpenAI API兼容,因此可以使用openai这个官方库。前往DeepSeek平台获取API Key。
2. 完整可运行代码
创建ai_chat_pysimplegui.py文件,替换your_deepseek_api_key_here。
import PySimpleGUI as sg
import openai # DeepSeek兼容OpenAI SDK
import threading
import json
from datetime import datetime
import os
# 设置GUI主题
sg.theme('LightBlue3')
sg.set_options(font=("Microsoft YaHei", 10))
class DeepSeekChatApp:
def __init__(self):
# ========== 1. 初始化DeepSeek客户端 ==========
self.client = openai.OpenAI(
api_key="your_deepseek_api_key_here", # 替换为你的DeepSeek API Key
base_url="https://api.deepseek.com" # DeepSeek API端点
)
# ========== 2. 对话历史与配置 ==========
self.messages = [
{"role": "system", "content": "You are DeepSeek AI, a helpful assistant."}
]
self.model = "deepseek-chat" # 可选: deepseek-coder, deepseek-reasoner
self.max_history = 10 # 保留最近10轮对话(防止上下文过长)
# ========== 3. 创建主窗口 ==========
self.window = self.create_main_window()
def create_main_window(self):
"""使用PySimpleGUI创建主窗口布局"""
# 左侧聊天历史区域
chat_history_column = [
[sg.Text("💭 DeepSeek AI 对话助手", font=("Microsoft YaHei", 14, "bold"))],
[sg.Multiline(
size=(70, 30),
key="-CHAT_HISTORY-",
autoscroll=True,
disabled=True,
background_color="#FAFAFA",
text_color="#333333",
font=("Consolas", 10)
)],
[sg.Text("当前模型: "),
sg.Combo(["deepseek-chat", "deepseek-coder"],
default_value="deepseek-chat",
key="-MODEL-",
enable_events=True,
readonly=True)]
]
# 右侧控制面板
control_panel_column = [
[sg.Text("对话控制", font=("Microsoft YaHei", 12, "bold"))],
[sg.Button("导出对话", key="-EXPORT-", size=(12, 1))],
[sg.Button("导入对话", key="-IMPORT-", size=(12, 1))],
[sg.Button("清空历史", key="-CLEAR-", size=(12, 1), button_color=("white", "red"))],
[sg.HorizontalSeparator()],
[sg.Text("参数设置", font=("Microsoft YaHei", 12, "bold"))],
[sg.Text("温度 (0.0-2.0):")],
[sg.Slider(range=(0.0, 2.0),
default_value=0.7,
resolution=0.1,
orientation='h',
key="-TEMPERATURE-",
size=(20, 15))],
[sg.Text("最大生成长度:")],
[sg.Input(default_text="1024", key="-MAX_TOKENS-", size=(10, 1))],
[sg.HorizontalSeparator()],
[sg.Text("会话信息", font=("Microsoft YaHei", 10))],
[sg.Multiline("对话已就绪...
",
size=(30, 5),
key="-STATUS-",
disabled=True,
autoscroll=True)]
]
# 底部输入区域
input_area = [
[sg.Multiline(
size=(85, 4),
key="-USER_INPUT-",
enter_submits=False, # 禁用回车提交,改用Ctrl+Enter
tooltip="输入消息,按 Ctrl+Enter 发送",
font=("Microsoft YaHei", 11)
)],
[sg.Button("发送 (Ctrl+Enter)", key="-SEND-", size=(15, 1), button_color=("white", "#4CAF50")),
sg.Button("退出", key="-EXIT-", size=(10
----
## 参考来源
- [python调用火山引擎大模型实现人工智能语音聊天机器人](https://blog.csdn.net/xingdiango/article/details/148231867)
- [Python调用智谱API实现简单AI对话框](https://blog.csdn.net/qq_68076599/article/details/143243079)
- [《AI大模型趣味实战》教10岁儿童使用Python调用智谱AI大模型创建图形化AI聊天机器人的教程](https://blog.csdn.net/yweng18/article/details/147477146)
- [[AI]从零开始的DeepSeek本地部署及本地API调用教程](https://blog.csdn.net/c858845275/article/details/145544208)
- [Python调用DeepSeek API实现图形化窗口](https://blog.csdn.net/aaaa11111112/article/details/146115612)
- [调用蓝耘Maas平台大模型API打造个人AI助理实战](https://blog.csdn.net/2301_80840905/article/details/148317768)
更多推荐


所有评论(0)