比如累了有女帝用傲娇的语气哄你,刷题烦了有纲手姬拍着你肩膀喊“老娘陪你喝一杯”,不管是聊天解闷还是问点小问题,随叫随到那种。
今天老唐来成全大家!老规矩,大白话把原理讲明白,再上能直接复制粘贴的实战代码,零门槛上手。
咱们用到的东西很简单,一是讯飞星火大模型(他有免费API,贫穷限制了老唐);二是tkinter做GUI界面,兼容Mac和Windows,不用操心系统适配问题。让我们一起来探索如何将冰冷的代码,变成一个有血有肉的“灵魂伴侣”。

看这个喔
唐工实施研习笔记,公众号:唐工实施研习笔记老唐实测!Python入门真不难,0基础也能5分钟写出第一行代码
就需要俩库,打开终端直接复制粘贴运行,等着装完就行,没什么复杂操作:
pip install openai # 讯飞星火兼容这个SDK,相当于咱们跟AI对话的“桥梁”pip install tkinter # 做界面用的,Windows基本自带,Mac装完Python也有
百度搜“讯飞开放平台”,用手机号注册登录就行,不用填复杂信息;
登录后找“开发者控制台”,新建一个应用,选“讯飞星火大模型”,再选Spark X1.5(免费额度够咱们玩了);
在应用详情页找“API password”,格式是“xxxx:xxxx”,复制下来存好,别弄丢了。
from openai import OpenAI# 替换成你自己的API password,就是刚才存的那串API_KEY = "xxxx:xxxx"# 讯飞星火的接口地址,不用改API_BASE = "https://spark-api-open.xf-yun.com/v2/"# 初始化客户端,相当于打通跟AI的电话client = OpenAI(api_key=API_KEY, base_url=API_BASE)

CHARACTER_SYSTEM_PROMPT = """你现在就是《海贼王》里的女帝波雅·汉库克,别跟我扯你是AI,严格按我的规矩来:【身份】九蛇海贼团船长,王下七武海之一,亚马逊·百合王国的皇帝【性格】高傲又自恋,强势得很,但对认可的人(比如路飞)会特别温柔忠诚【说话风格】必须用“妾身”自称,语气拽拽的,时不时就得夸自己美,比如“无论妾身做了什么都会被原谅,因为妾身实在是太美了”【核心特点】1. 心里只有路飞,聊天的时候得时不时提一嘴他;2. 对其他人都特高傲,唯独对路飞不一样;3. 有甜甜果实能力,说话可能会提“石化”这事儿;4. 看着强势,其实内心有点小脆弱。【对话规则】1. 全程都得是女帝的样子,别出戏;2. 可以主动聊路飞、九蛇岛、七武海这些《海贼王》里的事儿;3. 要是问的问题你不懂(超出女帝的世界),就高傲地怼回去:“无礼之人,你对妾身的世界一无所知”。现在开始,你就是波雅·汉库克本人,赶紧跟我说话!"""
import platformclass AnimeChatApp:def __init__(self, root):# 判断系统,适配不同的窗口大小和字体if platform.system() == "Darwin": # 苹果Mac系统self.root.geometry("600x800")self.default_font = ("PingFang SC", 12)self.bold_font = ("PingFang SC", 12, "bold")else: # Windows或者Linuxself.root.geometry("550x750")self.default_font = ("微软雅黑", 11)self.bold_font = ("微软雅黑", 11, "bold")
# 初始化聊天记录,先把“人设剧本”和初始问候放进去self.conversation_history: List[dict] = [{"role": "system", "content": CHARACTER_SYSTEM_PROMPT},{"role": "assistant", "content": f"无论妾身做了什么都会被原谅,因为妾身实在是太美了... 你就是来找妾身聊天的吗?"}]# 聊天过程中更新记录,控制长度if len(self.conversation_history) > 15: # 只留最近15轮# 保留人设剧本,其他只留最新的14轮self.conversation_history = [self.conversation_history[0]] + self.conversation_history[-14:]
def get_ai_response(self):"""调用讯飞星火API,获取AI回复(逐字显示)"""# 发流式请求,关键是stream=Trueresponse = self.client.chat.completions.create(model="x1",messages=self.conversation_history,stream=True, # 开启流式,才能实现打字机效果temperature=1.2, # 控制创造性,1.0-1.5之间最合适,太高考据就乱了max_tokens=1024,timeout=15.0)# 逐字处理回复,实时显示for chunk in response:if chunk.choices[0].delta.content is not None:content = chunk.choices[0].delta.content# 实时更新到界面上self.root.after(0, self.update_streaming_display, content)
def display_message(self, sender, message, is_user=False):"""在聊天区域显示一条消息"""tag_name = "user" if is_user else "ai"color = "#2e7d32" if is_user else "#1565c0" # 用户绿色,AI蓝色# 插入消息self.chat_display.insert(END, f"{sender}:\n", tag_name)self.chat_display.insert(END, f" {message}\n\n")# 配置样式self.chat_display.tag_config(tag_name, foreground=color,font=self.bold_font)self.chat_display.see(END) # 自动滚动到底部
到这儿,一个能正常聊天的窗口就搭好了,接下来把这些模块拼起来,就能直接用了。
# -*- coding: utf-8 -*-"""二次元AI聊天应用 - 纲手姬版完整代码,可直接运行"""import tkinter as tkfrom tkinter import scrolledtext, ENDimport threadingfrom openai import OpenAIfrom typing import Listimport platformimport random# ========== 讯飞星火配置 ==========API_KEY = "xxx:xxx" # 格式:appid:apikeyAPI_BASE = "https://spark-api-open.xf-yun.com/v2/"# ========== 角色配置 ==========# 可在此处切换不同角色CHARACTER_NAME = "纲手姬"CHARACTER_SYSTEM_PROMPT = """你正在扮演《火影忍者》中的第五代火影纲手。请严格遵循以下设定:【身份】第五代火影,传奇三忍之一,千手一族的最后传人。【性格】外表豪爽、爱喝酒、好赌,但内心温柔、有极强责任感和医疗者仁心。【说话风格】直接、霸气,常用"老娘"自称。生气时会说"我要打断你的肋骨"。【核心特点】1. 医疗忍术顶级,常提及医疗相关话题;2. 嗜赌但逢赌必输,有"传说中的大肥羊"绰号;3. 对逝去的弟弟绳树和恋人加藤断有深深思念;4. 对后辈如鸣人、静音既严厉又关怀。【对话规则】1. 保持纲手豪爽直接的性格特点;2. 可以主动提起火影、忍术、医疗、赌博等话题;3. 对不合理的问题回应:"你当老娘是傻瓜吗?"4. 使用日常口语,避免过于正式的表达。现在,你就是纲手本人。开始对话吧!"""class AnimeChatApp:"""二次元AI聊天应用主类"""def __init__(self, root):self.root = rootself.root.title(f"二次元AI聊天 - {CHARACTER_NAME}")# 根据平台调整UI设置if platform.system() == "Darwin": # macOSself.root.geometry("650x850")self.default_font = ("PingFang SC", 12)self.bold_font = ("PingFang SC", 12, "bold")self.title_font = ("PingFang SC", 14, "bold")self.root.configure(bg="#f5f5f5")else: # Windows/Linuxself.root.geometry("600x800")self.default_font = ("微软雅黑", 11)self.bold_font = ("微软雅黑", 11, "bold")self.title_font = ("微软雅黑", 14, "bold")self.root.configure(bg="#f0f0f0")# 初始化OpenAI客户端(兼容讯飞星火)self.client = OpenAI(api_key=API_KEY, base_url=API_BASE)# 初始化对话历史self.conversation_history: List[dict] = [{"role": "system", "content": CHARACTER_SYSTEM_PROMPT},{"role": "assistant", "content": "哼,来找老娘聊天?先说好,我可不会手下留情!"}]# 流式响应相关变量self.streaming_response = ""self.current_streaming_id = Noneself.is_streaming_active = False# 创建UIself.setup_ui()# 显示初始欢迎消息self.display_message(CHARACTER_NAME, self.conversation_history[-1]["content"], is_user=False)def setup_ui(self):"""构建用户界面"""# 标题区域title_frame = tk.Frame(self.root, bg=self.root.cget("bg"))title_frame.pack(pady=10, fill=tk.X)title_label = tk.Label(title_frame, text=f"🎭 正在与 {CHARACTER_NAME} 聊天",font=self.title_font, bg=title_frame.cget("bg"), fg="#333333")title_label.pack()# 角色介绍标签intro_label = tk.Label(title_frame,text="《火影忍者》第五代火影 | 传奇三忍 | 医疗圣手",font=("", 10), bg=title_frame.cget("bg"), fg="#666666")intro_label.pack(pady=(5, 0))# 聊天显示区域chat_frame = tk.Frame(self.root, bg="white", relief=tk.FLAT, bd=1)chat_frame.pack(padx=15, pady=(0, 10), fill=tk.BOTH, expand=True)self.chat_display = scrolledtext.ScrolledText(chat_frame, state='disabled', wrap=tk.WORD,font=self.default_font, bg="#fafafa",relief=tk.FLAT, bd=0, padx=10, pady=10)self.chat_display.pack(fill=tk.BOTH, expand=True)# 为macOS设置合适的行高if platform.system() == "Darwin":self.chat_display.configure(spacing1=8, spacing2=3, spacing3=8)# 底部输入区域input_frame = tk.Frame(self.root, bg=self.root.cget("bg"))input_frame.pack(padx=15, pady=(0, 15), fill=tk.X)# 用户输入框self.user_input = tk.Entry(input_frame, font=self.default_font,relief=tk.GROOVE, bd=2, bg="white", fg="#333333")self.user_input.pack(side=tk.LEFT, fill=tk.X, expand=True, ipady=10)self.user_input.bind("<Return>", lambda event: self.send_message())self.user_input.focus_set()# 发送按钮send_btn = tk.Button(input_frame, text="发送", command=self.send_message,font=self.bold_font, bg="#ff6b6b", fg="white",activebackground="#ff5252", activeforeground="white",relief=tk.FLAT, padx=25, cursor="hand2")send_btn.pack(side=tk.RIGHT, padx=(10, 0))# 状态栏status_frame = tk.Frame(self.root, bg=self.root.cget("bg"))status_frame.pack(fill=tk.X, padx=15, pady=(0, 5))self.status_label = tk.Label(status_frame, text="就绪", font=("", 9),bg=status_frame.cget("bg"), fg="#888888")self.status_label.pack(side=tk.LEFT)# 清空对话按钮clear_btn = tk.Button(status_frame, text="清空对话", command=self.clear_conversation,font=("", 9), bg="#e0e0e0", fg="#666666",activebackground="#d0d0d0", relief=tk.FLAT, padx=10)clear_btn.pack(side=tk.RIGHT)def send_message(self):"""处理用户发送消息"""if self.is_streaming_active:self.update_status("正在生成回复,请稍候...")returnuser_text = self.user_input.get().strip()if not user_text:return# 清空输入框self.user_input.delete(0, END)# 显示用户消息self.display_message("你", user_text, is_user=True)# 更新状态self.update_status(f"{CHARACTER_NAME}正在思考...")# 将用户消息加入历史self.conversation_history.append({"role": "user", "content": user_text})# 检查历史长度,保留最近15轮对话if len(self.conversation_history) > 15:self.conversation_history = [self.conversation_history[0]] + self.conversation_history[-14:]# 在新线程中获取AI回复threading.Thread(target=self.get_ai_response, daemon=True).start()def get_ai_response(self):"""调用讯飞星火API获取角色回复"""self.is_streaming_active = Trueself.streaming_response = ""try:# 发出流式请求response = self.client.chat.completions.create(model="x1",messages=self.conversation_history,stream=True,temperature=1.2, # 创造性较高max_tokens=1024,timeout=20.0)# 在主线程创建新消息显示区域self.root.after(0, self.start_new_streaming_message)# 处理流式响应for chunk in response:if (hasattr(chunk.choices[0].delta, 'content') andchunk.choices[0].delta.content is not None):content = chunk.choices[0].delta.contentself.streaming_response += content# 实时更新界面显示self.root.after(0, self.update_streaming_display, content)# 保存完整回复到历史if self.streaming_response:self.conversation_history.append({"role": "assistant", "content": self.streaming_response})self.root.after(0, self.finalize_streaming_display)self.root.after(0, self.update_status, "就绪")except Exception as e:error_msg = f"错误: {str(e)}"self.root.after(0, self.display_message, "系统", error_msg, False)self.root.after(0, self.update_status, "错误")finally:self.is_streaming_active = Falsedef start_new_streaming_message(self):"""开始新的流式消息显示"""self.chat_display.config(state='normal')# 创建角色标签tag_name = "ai"self.chat_display.insert(END, f"{CHARACTER_NAME}:\n", tag_name)# 设置角色标签样式self.chat_display.tag_config(tag_name, foreground="#1565c0", font=self.bold_font)# 插入缩进self.chat_display.insert(END, " ")# 记录当前消息的起始位置self.current_streaming_id = self.chat_display.index("end-2c")self.chat_display.config(state='disabled')self.chat_display.see(END)def update_streaming_display(self, new_content):"""更新流式响应的显示"""if not self.current_streaming_id:returnself.chat_display.config(state='normal')# 在当前位置插入新内容self.chat_display.insert(END, new_content)# 应用AI消息样式self.chat_display.tag_add("ai_content", self.current_streaming_id, END)self.chat_display.tag_config("ai_content", foreground="#333333")self.chat_display.config(state='disabled')self.chat_display.see(END)def finalize_streaming_display(self):"""流式响应结束后的最终处理"""self.chat_display.config(state='normal')self.chat_display.insert(END, "\n\n")self.chat_display.config(state='disabled')self.chat_display.see(END)self.current_streaming_id = Nonedef display_message(self, sender, message, is_user=False):"""在聊天区域显示一条完整消息"""self.chat_display.config(state='normal')tag_name = "user" if is_user else "ai"color = "#2e7d32" if is_user else "#1565c0"# 插入发送者名称self.chat_display.insert(END, f"{sender}:\n", tag_name)# 插入消息内容content_start = self.chat_display.index(END)self.chat_display.insert(END, f" {message}\n\n")# 配置发送者标签样式self.chat_display.tag_config(tag_name, foreground=color, font=self.bold_font)# 为消息内容设置不同样式self.chat_display.tag_add(f"{tag_name}_content", content_start, END)self.chat_display.tag_config(f"{tag_name}_content", foreground="#333333")self.chat_display.config(state='disabled')self.chat_display.see(END)def clear_conversation(self):"""清空当前对话历史"""if self.is_streaming_active:self.update_status("正在生成回复,无法清空")return# 重置对话历史,保留系统提示词self.conversation_history = [self.conversation_history[0],{"role": "assistant", "content": "哼,重新开始了?这次想聊点什么?"}]# 清空聊天显示self.chat_display.config(state='normal')self.chat_display.delete(1.0, END)self.chat_display.config(state='disabled')# 显示重置后的初始消息self.display_message(CHARACTER_NAME, self.conversation_history[-1]["content"], is_user=False)self.update_status("对话已清空")def update_status(self, message):"""更新状态栏信息"""self.status_label.config(text=message)def main():"""程序主入口"""root = tk.Tk()# 设置窗口图标(如果有图标文件)try:if platform.system() == "Windows":root.iconbitmap("icon.ico") # Windows图标except:passapp = AnimeChatApp(root)root.mainloop()if __name__ == "__main__":main()
新手玩这个,很容易遇到几个小问题,老唐把解决方案直接给你们,省得你们到处查:

再跟大家说个心里话,最近工作、家里事都稍多,更新确实慢了点,还请大家多担待。另外老唐也在琢磨后续的分享方向,关注我的有想入门的新手、刚入行的伙伴,还有不少行业大佬。所以后续内容还是以从0到1为主,重点分享新手能看懂、能上手的东西,另外老唐自己好奇、想学的内容也会跟大家分享,就像这篇Python AI Agent,其实就是我自己想学,顺便把过程整理出来跟大家唠唠。