从零构建AI电子宠物:大模型API、状态机与桌面应用实战
2026/9/6 12:26:36 网站建设 项目流程

各位朋友好,最近看到很多AI比赛里都冒出了各种让人眼前一亮(或者说眼前一黑)的创意项目。正好我在折腾一个标题特别“自嘲”的作品——开发一个“没用的AI”:电子宠物还能这样???

起初我以为这只是个乐子项目,但实际动手后发现,把一个“没用”的AI电子宠物真正跑起来,里面涉及的知识点相当杂:桌面应用开发、大模型API接入、上下文记忆、状态管理、多线程通信,甚至还有一点嵌入式硬件的玩法。本文将把整个项目的设计思路、核心代码和踩坑记录完整拆解出来。

如果你是AI应用开发新手,想找个不那么严肃的练手项目;如果你已经会调API,但不知道怎么做成有交互感的“产品”;或者你只是想看看比赛作品背后到底怎么实现,这篇文章都适合你。

需要提前说明的是,所谓“没用的AI”并不是真的没用,而是拒绝堆砌生产力功能,回归陪伴和趣味属性。项目跑起来后,可以跟桌面上的AI宠物聊天、看它打瞌睡、看它因为你的冷落而闹情绪,甚至让它“偷偷学习”你的作息规律。

下面开始完整的开发复盘。

1. 背景与核心概念

1.1 为什么叫“没用的AI”

市面上大多数AI应用都在强调效率和生产力:写邮件、写代码、画图、做PPT。但这个项目的出发点完全相反——只做一个陪伴型电子宠物,越“没用”越好,越像真实的宠物越好。

真正的宠物不会帮你写周报,它只会在你加班时趴在键盘旁,在你长时间不跟它互动时闹脾气,在你叫它名字时竖起耳朵。那么,如果把这些行为用大模型驱动起来,会是什么效果?

这个想法放到技术层面,其实是一个典型的“AI Agent + 情感计算 + 事件驱动”的小型综合体。它不是一个聊天机器人,而是一个有“状态”、有“情绪”的虚拟生命体。

1.2 电子宠物 + AI 到底在做什么

传统电子宠物(比如早期拓麻歌子)的行为逻辑是预设好的状态机:喂食、睡觉、玩耍、清洁,几个状态按固定规则切换。

AI电子宠物的区别在于:行为不是预设死板的规则,而是由大模型根据上下文实时生成。你可以自由跟它对话,它会根据性格设定、记忆、情绪状态来回复,并表现出相应的动画和动作。

为了便于理解,可以把系统拆成这几个层次:

层次职责技术实现
表现层宠物动画、气泡对话框、声音反馈Tkinter / PyQt / 前端Canvas
状态层情绪值、体力值、亲密度、记忆轮播状态机 + 定时任务
智能层对话生成、情绪推理、行为决策大模型 API + Prompt 工程
记忆层短期会话上下文、长期用户偏好SQLite / JSON / 向量数据库
事件层鼠标键盘活动检测、定时器事件、用户操作pynput / 系统事件监听

在这个架构下,AI电子宠物就不仅仅是一个“能聊天的窗口”,而是一个真正能感知周围环境、维持内部生理状态并影响对话风格的小系统。

1.3 应用场景和适合人群

这个项目适用的场景包括:

  • 个人桌面陪伴工具:在工作间隙获得一点“无用但治愈”的反馈。
  • 学习AI应用开发的入门项目:覆盖了工程化的大部分环节,但难度适中。
  • 比赛演示项目:因为有趣、直观、演示效果好,特别适合列入AI创意赛赛道。
  • 智能硬件DIY的前置实验:如果不做桌面宠物,可以把代码迁移到树莓派加上小屏幕做成实体机器人。

读这篇文章,你不需要先成为大模型专家,也不需要是桌面开发高手。只要会基础的Python语法,能理解函数和类,就可以跟着一步步把系统搭起来。

2. 环境准备与项目结构

2.1 开发环境版本说明

本次项目示例的开发环境如下:

  • 操作系统:Windows 11 / macOS 均可(代码做了跨平台处理)
  • Python:3.10+
  • 大模型API:以OpenAI兼容接口为例(国内厂商的同类接口基本兼容)
  • GUI框架:Tkinter(Python自带)
  • 事件监听库:pynput
  • 数据库:SQLite(Python内置)

需要说明的是,不同大模型厂商的API版本变化较快。本文示例代码以“OpenAI兼容的/v1/chat/completions接口”为例,你需要在代码中替换为自己的API_KEYBASE_URL。如果你用的是其他大模型平台,只需调整请求格式即可。

2.2 安装依赖

创建项目虚拟环境并安装依赖:

# 创建并激活虚拟环境 python -m venv venv source venv/bin/activate # Windows 下为 venv\Scripts\activate # 安装依赖 pip install requests pynput

Tkinter是Python标准库的一部分,一般不需要额外安装。如果在Linux环境,可能需要执行:

sudo apt-get install python3-tk

2.3 项目目录结构

推荐按模块化方式组织项目,方便后续扩展:

useless-ai-pet/ │ ├── main.py # 程序入口,启动GUI与主循环 ├── config.py # 配置文件:API密钥、模型参数、宠物性格 ├── pet.py # 宠物状态管理(情绪、体力、亲密度) ├── brain.py # AI大脑:对话生成、行为决策 ├── memory.py # 记忆模块:SQLite长期记忆 ├── desktop_pet.py # 桌面宠物窗口(Tkinter实现) ├── listener.py # 鼠标键盘事件监听 │ ├── images/ # 宠物动画素材(若无素材可用Canvas绘制) │ ├── idle.png │ └── happy.png │ ├── data/ # 运行时产生的数据库文件 │ └── pet_memory.db │ ├── requirements.txt # 依赖列表 └── README.md

先梳理整体结构,接下来逐个模块实现。本文按照从“底层状态”到“上层表现”的顺序,先实现宠物状态管理,再接入AI大脑。

3. 核心模块设计

3.1 宠物状态机设计

宠物是“活”的,必须有状态变化。在这个项目里定义了三个核心指标:

  • 情绪值(0~100):影响对话语气和动画。
  • 体力值(0~100):影响宠物是否愿意理你,决定是否进入休眠。
  • 亲密度(0~100):长期交互积累的数值,越亲密回复越“放肆”。

状态变化逻辑:

  • 用户主动与宠物互动:情绪值+5,亲密度+2。
  • 长时间未互动:情绪值-2/分钟,体力值-1/分钟。
  • 体力值低于20:宠物进入“爱答不理”模式。
  • 情绪值低于30:宠物会主动抱怨。

用一个Python类来管理:

# pet.py class PetState: def __init__(self, name="小废物"): self.name = name self.mood = 70 # 情绪 0~100 self.energy = 80 # 体力 0~100 self.intimacy = 10 # 亲密度 0~100 self.is_sleeping = False def tick(self, minutes=1): """每分钟调用一次,模拟时间流逝带来的状态变化""" if self.is_sleeping: self.energy = min(100, self.energy + 2) return self.mood = max(0, self.mood - 2) self.energy = max(0, self.energy - 1) if self.energy < 20: self.mood -= 1 def interact(self): """用户互动时调用""" self.mood = min(100, self.mood + 5) self.intimacy = min(100, self.intimacy + 2) self.energy = max(0, self.energy - 1) def get_state_text(self): """把状态翻译成自然语言,便于放到Prompt中""" if self.is_sleeping: return f"{self.name}正在呼呼大睡" if self.mood > 80: return f"{self.name}心情非常好" if self.mood < 30: return f"{self.name}心情低落到不想说话" if self.energy < 20: return f"{self.name}又累又困,反应很迟钝" return f"{self.name}情绪稳定,状态正常"

3.2 AI大脑:不止是聊天

AI大脑模块负责两件事:一是根据宠物状态生成带性格的对话回复;二是通过大模型推理,让宠物“自己决定”当前行为(趴着、打滚、要吃的、睡觉)。

为此设计了两套Prompt:

对话Prompt负责角色扮演:

# brain.py(部分示例) system_prompt_template = """ 你是一只电子宠物,名字叫{name}。 你的性格是:{personality} 你当前的状态:{state_text} 你和主人的亲密度:{intimacy}/100 规则: 1. 你的回复必须简短自然,不超过50个字。 2. 如果状态差,你会表达疲惫或不耐烦;如果状态好,你会很活泼。 3. 不要总是讨好人,要有自己的脾气。 4. 回复中不要使用表情符号。 当前是{hour}点{minute}分,主人对你说:{user_input} """

行为决策Prompt负责判断下一秒动作:

behavior_prompt_template = """ 根据宠物当前的状态,从下列行为中选择一个最合适的,只输出一个词: [idle] 安静趴着 [play] 兴奋地转圈 [sleep] 准备睡觉 [hungry] 想吃东西 [angry] 生气不理人 宠物状态:{state_text} 最近互动:{recent_interaction} """

行为决策相当于给大模型一个小的“动作槽位”,让宠物看起来是“自己”决定做什么,而不是代码写死的。

为了节省API调用成本,不会每次对话都重新调用行为决策接口,而是每10分钟或状态发生较大变化时调用一次。

3.3 短期会话上下文

为了让对话有连续感,需要维护一个会话历史窗口。这里直接用一个队列保存最近10轮对话:

# brain.py(补充) from collections import deque class ConversationMemory: def __init__(self, maxlen=10): self.history = deque(maxlen=maxlen) def add(self, role, content): self.history.append({"role": role, "content": content}) def to_list(self): return list(self.history)

3.4 长期记忆持久化

长期记忆使用SQLite保存。记忆分两类:宠物对主人的事实记忆(比如主人喜欢深夜编程,主人在养猫);宠物自身的“经历”记忆(比如昨天跟主人聊了离职话题)。

# memory.py import sqlite3 import json from datetime import datetime class MemoryStore: def __init__(self, db_path="data/pet_memory.db"): self.conn = sqlite3.connect(db_path) self._create_tables() def _create_tables(self): self.conn.execute(""" CREATE TABLE IF NOT EXISTS facts ( id INTEGER PRIMARY KEY AUTOINCREMENT, fact TEXT NOT NULL, created_at TEXT DEFAULT (datetime('now', 'localtime')) ) """) self.conn.execute(""" CREATE TABLE IF NOT EXISTS chat_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, role TEXT NOT NULL, content TEXT NOT NULL, created_at TEXT DEFAULT (datetime('now', 'localtime')) ) """) self.conn.commit() def save_fact(self, fact): self.conn.execute("INSERT INTO facts (fact) VALUES (?)", (fact,)) self.conn.commit() def get_recent_facts(self, limit=5): cursor = self.conn.execute( "SELECT fact FROM facts ORDER BY id DESC LIMIT ?", (limit,) ) return [row[0] for row in cursor.fetchall()] def log_chat(self, role, content): self.conn.execute( "INSERT INTO chat_logs (role, content) VALUES (?, ?)", (role, content) ) self.conn.commit()

这里没有做向量检索,因为示例项目的记忆体量不大。如果未来要支持长期海量记忆,可以引入向量数据库,这里先用SQLite保证逻辑清晰。

4. 完整实战:让AI电子宠物跑起来

4.1 创建项目结构和配置文件

首先创建config.py文件,把API配置和宠物性格设定集中起来:

# config.py import os # 大模型API配置 # 请将下面的API_KEY替换为你的真实密钥 API_KEY = os.getenv("LLM_API_KEY", "your-api-key-here") BASE_URL = os.getenv("LLM_BASE_URL", "https://api.openai.com/v1") MODEL = os.getenv("LLM_MODEL", "gpt-3.5-turbo") # 宠物基本信息 PET_NAME = "小废物" PET_PERSONALITY = """ 你是一只外表呆萌但内心傲娇的电子宠物。 你嘴上说“我才懒得理你”,但主人太久不理你又会焦虑。 你喜欢观察主人的生活细节,偶尔毒舌,但骨子里关心主人。 """ # 状态更新间隔(秒) TICK_INTERVAL = 60 # 行为决策间隔(秒) BEHAVIOR_INTERVAL = 600

这里推荐使用环境变量传入API密钥,避免硬编码泄露。在运行前:

export LLM_API_KEY="你的密钥" export LLM_BASE_URL="你的接口地址" export LLM_MODEL="模型名称"

Windows下请使用:

set LLM_API_KEY=你的密钥

4.2 编写AI大脑模块

接着实现brain.py,包括对话和决策两部分:

# brain.py import requests import json from config import API_KEY, BASE_URL, MODEL, PET_NAME, PET_PERSONALITY from pet import PetState class PetBrain: def __init__(self, pet_state: PetState): self.pet_state = pet_state self.conversation = ConversationMemory() self.headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } def _call_llm(self, messages, temperature=0.8): """调用大模型接口,返回文本回复""" payload = { "model": MODEL, "messages": messages, "temperature": temperature, "max_tokens": 200 } try: resp = requests.post( f"{BASE_URL}/chat/completions", headers=self.headers, json=payload, timeout=15 ) resp.raise_for_status() data = resp.json() return data["choices"][0]["message"]["content"].strip() except Exception as e: print(f"[brain] 大模型调用失败: {e}") return "(小废物歪了歪头,似乎没听懂你在说什么。)" def chat(self, user_input: str): """处理用户对话""" # 用户互动,更新状态 self.pet_state.interact() # 提取长期记忆 facts = self._load_facts() # 构造系统提示词 system_prompt = f""" 你是一只电子宠物,名字叫{PET_NAME}。 你的性格是:{PET_PERSONALITY} 你当前的状态:{self.pet_state.get_state_text()} 你和主人的亲密度:{self.pet_state.intimacy}/100 你还记得的关于主人的事情: {chr(10).join('- ' + f for f in facts) if facts else '(还没有太多记忆)'} 规则: 1. 你的回复必须简短自然,不超过50个字。 2. 如果状态差,你会表达疲惫或不耐烦;如果状态好,你会很活泼。 3. 不要总是讨好人,要有自己的脾气,可以偶尔傲娇。 4. 回复中不要使用表情符号。 """ messages = [{"role": "system", "content": system_prompt}] messages.extend(self.conversation.to_list()) messages.append({"role": "user", "content": user_input}) reply = self._call_llm(messages) self.conversation.add("user", user_input) self.conversation.add("assistant", reply) return reply def decide_behavior(self): """让大模型从候选动作中选择一个""" behavior_prompt = f""" 根据宠物当前的状态,从下列行为中选择一个最合适的,只输出一个词,不要输出其他内容: [idle] 安静趴着 [play] 兴奋地转圈 [sleep] 准备睡觉 [hungry] 想吃东西 [angry] 生气不理人 宠物状态:{self.pet_state.get_state_text()} 最近一次互动记录:{self.conversation.to_list()[-1] if self.conversation.to_list() else '无'} """ messages = [ {"role": "system", "content": "你是一个宠物行为决策引擎。"}, {"role": "user", "content": behavior_prompt} ] action = self._call_llm(messages, temperature=0.3) valid_actions = ["idle", "play", "sleep", "hungry", "angry"] if action not in valid_actions: return "idle" return action def _load_facts(self): # 避免循环依赖,延迟导入 from memory import MemoryStore try: store = MemoryStore() return store.get_recent_facts(3) except Exception: return []

4.3 桌面宠物窗口实现

桌面宠物窗口沿用Tkinter实现。为了减少对素材的依赖,这里用Canvas绘制一个“方形废柴宠物”,后续可以替换图片资源。

# desktop_pet.py import tkinter as tk import random class DesktopPet: def __init__(self, root, brain): self.root = root self.brain = brain self.action = "idle" # 窗口设置:置顶、无边框、透明 self.root.overrideredirect(True) self.root.attributes("-topmost", True) try: self.root.attributes("-transparentcolor", "white") except tk.TclError: pass self.canvas = tk.Canvas(self.root, width=220, height=220, bg="white") self.canvas.pack() # 绘制主体 self.body = self.canvas.create_oval(50, 70, 170, 190, fill="#f0c27f", outline="black") self.eye_left = self.canvas.create_oval(85, 100, 105, 120, fill="black") self.eye_right = self.canvas.create_oval(135, 100, 155, 120, fill="black") self.mouth = self.canvas.create_arc(100, 130, 140, 160, start=0, extent=-180, style=tk.ARC) # 拖拽移动 self.canvas.bind("<ButtonPress-1>", self.on_drag_start) self.canvas.bind("<B1-Motion>", self.on_drag_move) # 双击隐藏(右键菜单退出) self.canvas.bind("<Double-Button-1>", self.on_double_click) self.update_loop() def on_drag_start(self, event): self._drag_x = event.x self._drag_y = event.y def on_drag_move(self, event): x = self.root.winfo_x() + event.x - self._drag_x y = self.root.winfo_y() + event.y - self._drag_y self.root.geometry(f"+{x}+{y}") def on_double_click(self, event): self.root.destroy() def set_action(self, action): """根据行为切换动画表现""" self.action = action self._render_action() def _render_action(self): if self.action == "play": # 宠物兴奋时眼睛变大 self.canvas.coords(self.eye_left, 80, 95, 110, 125) self.canvas.coords(self.eye_right, 130, 95, 160, 125) self.canvas.itemconfig(self.body, fill="#f7d794") elif self.action == "sleep": # 睡觉时眼睛变成横线 self.canvas.coords(self.eye_left, 85, 110, 105, 112) self.canvas.coords(self.eye_right, 135, 110, 155, 112) self.canvas.itemconfig(self.body, fill="#a4b0be") elif self.action == "angry": self.canvas.itemconfig(self.body, fill="#ff6b6b") # 眉毛(用线段示意) self.canvas.delete("brow") self.canvas.create_line(82, 95, 108, 102, fill="black", width=2, tags="brow") self.canvas.create_line(138, 102, 158, 95, fill="black", width=2, tags="brow") else: # idle 状态 self.canvas.delete("brow") self.canvas.coords(self.eye_left, 85, 100, 105, 120) self.canvas.coords(self.eye_right, 135, 100, 155, 120) self.canvas.itemconfig(self.body, fill="#f0c27f") def show_message(self, text): """显示对话气泡""" self.canvas.delete("bubble") self.canvas.delete("bubble_text") # 绘制简单气泡 bubble = self.canvas.create_rectangle( 20, 10, 200, 60, fill="white", outline="black", width=1, tags="bubble" ) self.canvas.create_text( 110, 35, text=text, width=170, tags="bubble_text", font=("Microsoft YaHei", 9), anchor="center" ) # 3秒后自动消失 self.root.after(3000, lambda: self.canvas.delete("bubble", "bubble_text")) def update_loop(self): """定时更新宠物行为""" # 每30秒重新决策 if random.random() < 0.3: action = self.brain.decide_behavior() self.set_action(action) self.root.after(5000, self.update_loop)

4.4 鼠标键盘事件监听

为了让宠物感知到“人类存在”,用pynput监听鼠标键盘活动。一旦有活动,就认为主人在附近,适当调整状态变化速率。

# listener.py from pynput import mouse, keyboard import threading import time class ActivityListener: def __init__(self, pet_state, pet_window): self.pet_state = pet_state self.pet_window = pet_window self.last_activity_time = time.time() def start(self): def on_move(x, y): self._register_activity() def on_click(x, y, button, pressed): self._register_activity() def on_key_press(key): self._register_activity() mouse_listener = mouse.Listener(on_move=on_move, on_click=on_click) keyboard_listener = keyboard.Listener(on_press=on_key_press) mouse_listener.start() keyboard_listener.start() def _register_activity(self): now = time.time() # 如果距离上次活动超过120秒,则算作“主人回来了” if now - self.last_activity_time > 120: self.pet_window.show_message("哦?你还知道回来啊……") self.pet_state.interact() self.last_activity_time = time.time() def get_idle_seconds(self): return int(time.time() - self.last_activity_time)

4.5 主程序入口

最后编写main.py串联所有模块:

# main.py import tkinter as tk import threading import time from config import TICK_INTERVAL, BEHAVIOR_INTERVAL from pet import PetState from brain import PetBrain from desktop_pet import DesktopPet from listener import ActivityListener from memory import MemoryStore def tick_loop(root, pet_state, pet_window, brain): """后台状态更新线程""" while True: time.sleep(TICK_INTERVAL) pet_state.tick() # 根据状态定时显示气泡 if pet_state.energy < 20 and not pet_state.is_sleeping: root.after(0, lambda: pet_window.show_message("好困……我要睡着了……")) pet_state.is_sleeping = True elif pet_state.energy > 50 and pet_state.is_sleeping: pet_state.is_sleeping = False if pet_state.mood < 30: root.after(0, lambda: pet_window.show_message("我不想说话,别烦我。")) def main(): # 初始化核心对象 pet_state = PetState() brain = PetBrain(pet_state) memory = MemoryStore() # 创建窗口 root = tk.Tk() pet_window = DesktopPet(root, brain) # 启动事件监听 listener = ActivityListener(pet_state, pet_window) listener.start() # 启动状态更新线程 tick_thread = threading.Thread(target=tick_loop, args=(root, pet_state, pet_window, brain), daemon=True) tick_thread.start() # 进入Tk主循环 root.mainloop() if __name__ == "__main__": main()

4.6 运行与验证

运行:

python main.py

预期效果:

  • 桌面右上角出现一个圆滚滚的贴图宠物,窗口置顶。
  • 双击宠物窗口,程序退出。
  • 用鼠标拖动窗口,可以移动。
  • 暂时没有输入框,意味着还无法对话。下一节补充对话栏和交互逻辑。

4.7 加入对话交互

要让用户能与宠物聊天,需要添加一个输入框。为了让Tkinter窗口显示输入框,可调整DesktopPet类,增加底部输入区域:

# desktop_pet.py 中增加以下方法 def add_chat_entry(self, on_send_callback): """在窗口底部添加输入框""" self.entry_frame = tk.Frame(self.root, bg="white") self.entry_frame.pack(side=tk.BOTTOM, fill=tk.X) self.entry = tk.Entry(self.entry_frame, font=("Microsoft YaHei", 10)) self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=4, pady=4) self.entry.bind("<Return>", lambda e: self._send_chat(on_send_callback)) send_btn = tk.Button( self.entry_frame, text="说", command=lambda: self._send_chat(on_send_callback), font=("Microsoft YaHei", 9) ) send_btn.pack(side=tk.RIGHT, padx=4) # 让窗口更宽以容纳输入框 self.canvas.config(width=280, height=220) self.root.geometry("280x260") # 输入框常驻置顶 self.entry.focus_set() def _send_chat(self, on_send_callback): text = self.entry.get().strip() if text: self.entry.delete(0, tk.END) on_send_callback(text)

main.pymain()中添加:

# 在 pet_window 创建后 def handle_user_input(text): reply = brain.chat(text) pet_window.show_message(reply) memory.log_chat("user", text) memory.log_chat("assistant", reply) pet_window.add_chat_entry(handle_user_input)

这样宠物就能简单对话了。

4.8 添加长期记忆提取功能

要让宠物真正“记住”主人的事情,可以在聊天的同时提示大模型抽取事实,并保存到记忆库:

# memory.py 增加以下方法 def extract_and_save_facts(self, user_text, assistant_text): """简易规则抽取:对话中出现的个人信息关键词 (完整项目建议通过大模型抽取,这里演示规则方式) """ keywords = ["我的", "我喜欢", "我在", "我想", "我有"] for kw in keywords: if kw in user_text: # 截取包含关键词的短句作为事实 idx = user_text.index(kw) fact = user_text[idx:idx+30] self.save_fact(fact) return

main.py中调用:

def handle_user_input(text): reply = brain.chat(text) pet_window.show_message(reply) memory.log_chat("user", text) memory.log_chat("assistant", reply) memory.extract_and_save_facts(text, reply)

5. 常见问题与排查思路

5.1 大模型API调用失败,宠物只会“歪头”

问题现象常见原因解决思路
对话后宠物不回复,或一直返回兜底文案API_KEY错误检查环境变量是否设置正确
请求超时网络不通或接口域名填错确认BASE_URL是否与API文档一致
返回401授权头格式不对检查Authorization是否为Bearer 你的key
返回404模型名称不对确认MODEL与平台实际提供的模型一致

排查步骤:

  1. 打印请求的完整载荷,确认messages格式。
  2. 用curl命令单独测试API接口:
curl {BASE_URL}/chat/completions \ -H "Authorization: Bearer {API_KEY}" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-3.5-turbo","messages":[{"role":"user","content":"你好"}]}'
  1. 如果curl成功但代码失败,检查Python环境中requests是否安装。

5.2 宠物窗口透明效果失效

问题现象常见原因解决思路
窗口中白色背景不能透明Tkinter不支持透明色使用wm_attributes("-transparentcolor", "black"),把底色设为纯黑
macOS上透明无效macOS不支持Tkinter透明色改用PyQt或换肤方案
窗口不是置顶系统权限限制检查窗口管理器中置顶设置,或尝试更底层的工具库

5.3 鼠标键盘监听导致程序卡死或无响应

问题大多数是因为在监听回调中执行了耗时操作(比如同步调用大模型API)。

解决方案:所有AI请求都必须放到子线程或放入队列中,不要在pynput的回调中阻塞。

推荐做法:

import queue task_queue = queue.Queue() def on_key_press(key): # 只投递任务,不执行耗时操作 task_queue.put("user_activity") # 在工作线程中消费队列 def worker(): while True: task = task_queue.get() if task == "user_activity": # 处理逻辑 pass

5.4 长期记忆文件损坏或无法写入

SQLite数据库文件可能因为多线程写操作冲突导致“database is locked”错误。

解决方案

  • MemoryStore中使用check_same_thread=False并加写锁。
  • 或者将记忆模块的所有操作集中到单个线程执行。
  • 数据库路径data/目录确保已创建。
self.conn = sqlite3.connect(db_path, check_same_thread=False)

5.5 宠物动画不切换

行为决策接口返回的动作名不在合法列表内。可以在decide_behavior()中增加更宽松的匹配,比如包含“play”就看作“play”。

if "play" in action: return "play" elif "sleep" in action: return "sleep" elif "angry" in action: return "angry" elif "hungry" in action: return "hungry" return "idle"

6. 最佳实践与工程优化建议

6.1 Prompt 工程质量

这个项目里的Prompt是宠物“人格”的核心。建议把系统Prompt单独放到一个文本文件或配置项中,方便反复调试。

注意点:

  • 模型输出长度要限制在较短范围,不然宠物会变成话痨。
  • 每次对话前把宠物状态写入Prompt,让回复“有身体状况”。
  • 可以设定“禁止使用AI腔”来减少机械感。
  • 如果平台支持,可以尝试开启流式输出(stream),让对话像打字机一样逐字出现,交互感更强。

6.2 成本控制

大模型调用是需要成本的。哪怕是自己申请的API,频繁调用行为决策也会消耗大量token。

建议:

  • 行为决策不要每次对话都调用,改为计时触发。
  • 对话历史窗口不要太大,10轮左右合适。
  • 使用温度参数(temperature)区分对话和决策:对话用0.8,决策用0.3。
  • 可以对低资源环境加入本地小模型的降级方案(例如本地规则生成回复)。

6.3 多线程与界面更新

Tkinter界面只能在主线程更新,如果从后台线程直接调用canvas.itemconfig,会引发崩溃。

推荐方式:

  • 在子线程中通过root.after(0, callback)把UI更新操作交给主线程。
  • 所有耗时IO(API请求、数据库写入)都放到后台线程。
  • 使用queue.Queue做线程间通信。

6.4 数据安全与隐私

虽然这只是个“没用的AI”,但如果它存储了主人对话内容,依然要处理用户隐私数据。

建议:

  • 明确告知用户对话会被保存在本地数据库。
  • 数据库文件放在用户目录下,避免放在系统临时目录。
  • 提供“删除所有记忆”的功能。
  • 不要把API密钥硬编码到代码或提交到Git仓库。使用环境变量或本地配置文件,并加入.gitignore

6.5 音频与多模态扩展

如果想更进一步,可以接入TTS(文本转语音)能力。现在很多大模型平台都提供TTS接口,可以把宠物回复转成语音,配合动画效果,体验提升非常明显。

集成思路:

# 伪代码,示意TTS接入 def text_to_speech(text): audio_url = call_tts_api(text) play_audio(audio_url)

在对话回调中,当宠物回复后,自动调用TTS并播放。注意音频播放会阻塞进程,也需要放到子线程中。

6.6 项目目录与依赖管理

推荐使用requirements.txt锁定依赖版本:

requests==2.31.0 pynput==1.7.6

使用虚拟环境隔离依赖:

python -m venv venv source venv/bin/activate pip install -r requirements.txt

7. 总结与下一步方向

现在你应该已经掌握了一个“没用的AI电子宠物”从零到能跑的核心路径。它看起来不复杂,却串联了很多AI应用开发必不可少的能力:

  • 状态管理:让AI不只是在“文本层面”输出,还能拥有内部生理状态。
  • Prompt工程:通过不同层次的提示词,让模型以不同的角色和规则完成对话与行为决策。
  • 多线程架构:区分UI线程、AI请求线程、事件监听线程,保证界面流畅。
  • 记忆持久化:把短期会话和长期事实分开存储,让宠物有“记性”。
  • 事件驱动感知:通过鼠标键盘监听,让宠物感知到主人是否在场。

如果你想让这个项目进一步进化,可以考虑以下方向:

  1. 接入视觉能力:给宠物装一个“眼睛”,通过摄像头判断主人表情,让情绪状态更真实。
  2. 接入语音交互:用麦克风接收语音输入,用TTS输出声音,真正变成桌面口语伴聊AI。
  3. 本地模型部署:如果你的电脑有足够显存,可以换成本地部署的Qwen或Llama系列模型,实现完全离线运行。
  4. 实体化:把代码迁移到树莓派和一块小屏幕上,封装成桌面摆件,参加硬件类AI比赛。
  5. 多宠物交互:让两个AI宠物可以互相聊天,在家里上演“AI版老友记”。

坦白说,着手开发前我也以为这个项目太简单,结果从需求拆分、状态设计、接口联调到动画调试,前前后后还是花了不少时间。也正因为项目边界清晰,几乎每一个环节都能学到东西,非常适合作为AI应用开发的练手项目。

希望这篇文章能给那些也想做点“没什么用但很好玩”的AI项目的朋友一些启发。如果你照着做出来了一个自己的AI电子宠物,记得给它取个合适的名字——毕竟它会陪你度过很多写代码的夜晚。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询