从零开发AI智能桌宠:PyQt5+大模型打造桌面效率助手
当代打工人的桌面早已不只是工作台那么简单。白天效率工具开了七八个晚上还想有个小家伙在屏幕角落陪着自己。桌宠这个老概念最近因为 AI 的加入又火了起来——从单纯的“养在桌面上的小动物”开始变成能对话、能提醒、能帮你处理碎片任务的 AI 伙伴。本文就来完整拆解一个“打工人解压 AI 效率”方向的智能桌宠项目它到底能做什么、技术架构怎么设计、核心代码怎么写、踩过哪些坑、以及如何把它做成一个真正可用的桌面应用。无论你是刚接触桌宠开发的爱好者还是想给团队做一款内部效率小工具的开发者都能从这篇文章里拿到一套可直接落地的方案。1. 先搞清楚AI 桌宠到底是什么1.1 从传统桌宠到 AI 桌宠的进化桌宠Desktop Pet这个概念并不新鲜。早年的 Flash 桌宠、QQ 宠物、各种“养在桌面上”的小动物核心功能是陪伴、动画反馈、简单交互。用户喜欢它是因为在长时间面对电脑时有一个会动、会回应的角色能带来放松感。传统桌宠的问题也很明显交互深度有限基本靠点击触发固定动画不能理解用户说的话也不能主动帮用户做事。它更像是一个“电子手办”而不是“效率伙伴”。AI 桌宠则把大模型、语音识别、任务调用、日程提醒等能力塞进了这个小小的桌面角色里。它不再只是卖萌而是能听懂你的自然语言指令比如“帮我记一个待办”“今天下午提醒我开会”。主动提醒工作节点到点弹出气泡。根据你的状态给出情绪反馈比如长时间工作后催促你休息。调用系统能力或 API完成查天气、建日程、打开应用、整理文档等轻量操作。所以AI 桌宠的本质是“桌面 UI 大模型能力 本地工具调用”的结合体。1.2 适合哪些人使用上班族需要随时记录灵感、提醒事项又不想频繁切换效率软件。ADHD 人群注意力容易分散桌宠能提供低侵入的陪伴和间断提醒。开发者与产品经理想学习“AI 应用开发”“AI Agent”落地桌宠是很好的练手场景。体验型用户想给自己的电脑添加一点个性化的互动元素。本文选择的技术路线重点面向能理解代码、想亲手改造的开发者。如果你只是想要现成产品也可以按文中的架构去对比市面上的智能桌宠工具。1.3 核心功能范围一个“打工人解压 AI 效率”桌宠至少要包含以下能力功能模块说明优先级桌宠显示与交互动画悬浮窗、拖动、点击反馈必选AI 对话基于大模型的自然语言对话必选待办/提醒识别并保存待办定时提醒推荐系统控制打开应用、查天气、剪贴板操作进阶情绪与陪伴反馈根据对话内容或工作状态生成反馈可选2. 环境准备与版本说明2.1 技术选型思路桌宠开发有两个主要方向Web 技术栈Electron / Tauri 前端动画适合前端开发者跨平台方便。桌面原生技术Python PyQt / Tkinter开发快适合快速实现原型和 AI 脚本集成。考虑到本文重点是“AI 能力接入”和“桌宠交互逻辑”采用Python PyQt5 大模型 API的组合。这套方案的好处是PyQt5 可以轻松实现透明窗口、无边框悬浮、贴边隐藏。Python 生态调用大模型 API 非常方便。代码易读性高即使是初学者也能跟着改。如果想做生产级产品可以把客户端换成 Tauri体积小、内存低但核心的 AI Agent 逻辑依然可以用 Python 服务或本地进程实现。2.2 环境依赖本文示例环境以常见配置为例重点演示实现思路。实际版本需要根据你的环境调整。操作系统Windows 10/11macOS / Linux 也可以窗口参数略有差异 Python3.9 或更高版本 PyQt55.15.x requests2.31.x安装命令pip install PyQt5 requests2.3 大模型 API 准备AI 对话功能需要一个大模型接口。你可以选择国内大模型厂商的在线 API如通义、智谱、文心、讯飞星火等。本地部署的模型如通过 Ollama 运行 Qwen、Llama 等。云服务商提供的模型服务。本文代码中会封装一个LLMClient只依赖 HTTP 接口不绑定具体厂商。你只需要把api_key和endpoint替换成自己的即可。3. 核心功能拆解与代码实现3.1 桌面悬浮窗的实现要做一个桌宠第一步是让窗口“浮”在桌面上并且没有系统标题栏。# 文件路径desktop_pet/window.py from PyQt5.QtCore import Qt, QPoint from PyQt5.QtWidgets import QWidget, QLabel, QApplication from PyQt5.QtGui import QPixmap, QFont class PetWindow(QWidget): def __init__(self): super().__init__() self.init_window() self.init_ui() def init_window(self): # 无边框、透明背景、窗口置顶 self.setWindowFlags( Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool ) self.setAttribute(Qt.WA_TranslucentBackground) # 初始位置屏幕右下角 screen QApplication.primaryScreen().availableGeometry() self.resize(120, 120) self.move(screen.width() - 150, screen.height() - 180) def init_ui(self): self.label QLabel(self) self.label.setGeometry(0, 0, 120, 120) self.label.setPixmap(QPixmap(assets/pet_default.png).scaled(120, 120)) self.label.setAlignment(Qt.AlignCenter) font QFont(微软雅黑, 8) self.bubble QLabel(self) self.bubble.setFont(font) self.bubble.setStyleSheet( background-color: rgba(255,255,255,0.9); border-radius: 8px; padding: 6px; ) self.bubble.setWordWrap(True) self.bubble.setVisible(False) self.bubble.setGeometry(10, -50, 100, 45) # 支持拖动 def mousePressEvent(self, event): if event.button() Qt.LeftButton: self.drag_position event.globalPos() - self.frameGeometry().topLeft() event.accept() def mouseMoveEvent(self, event): if event.buttons() Qt.LeftButton and hasattr(self, drag_position): self.move(event.globalPos() - self.drag_position) event.accept()核心点FramelessWindowHint去掉标题栏。WA_TranslucentBackground让背景透明只显示 PNG 素材。WindowStaysOnTopHint让桌宠一直浮在窗口上层。拖动功能通过重写鼠标事件实现。3.2 气泡提示与状态反馈桌宠要有“存在感”就需要气泡消息。比如 AI 返回结果、系统提醒都可以显示在气泡里。# 文件路径desktop_pet/bubble.py from PyQt5.QtCore import QTimer def show_bubble(self, text, duration3000): 显示气泡 # 限制长度避免遮挡屏幕 if len(text) 60: text text[:60] ... self.bubble.setText(text) self.bubble.adjustSize() self.bubble.setVisible(True) # 自动隐藏 QTimer.singleShot(duration, lambda: self.bubble.setVisible(False))为了不打断思绪气泡默认只显示 3 秒。如果文本太长做截断处理。3.3 AI 对话客户端封装AI 桌宠最核心的模块是对话引擎。这里采用通用 HTTP 封装方便切换模型服务。# 文件路径desktop_pet/llm_client.py import requests import json class LLMClient: def __init__(self, api_key, endpoint, modelqwen-plus): self.api_key api_key self.endpoint endpoint self.model model self.history [] def chat(self, user_message, system_prompt): 调用大模型接口返回文本回答 messages [] if system_prompt: messages.append({role: system, content: system_prompt}) for item in self.history[-10:]: # 控制上下文长度 messages.append(item) messages.append({role: user, content: user_message}) payload { model: self.model, messages: messages, temperature: 0.7, } headers { Content-Type: application/json, Authorization: fBearer {self.api_key}, } try: resp requests.post(self.endpoint, jsonpayload, headersheaders, timeout30) resp.raise_for_status() data resp.json() reply data[choices][0][message][content] # 保存历史 self.history.append({role: user, content: user_message}) self.history.append({role: assistant, content: reply}) return reply except Exception as e: return fAI 接口调用失败{e}代码要点保留最近 10 轮对话防止上下文过长超出模型限制。超时时间设为 30 秒避免无响应卡住桌宠。如果解析结构不同请根据实际返回格式修改data[choices]部分。endpoint和api_key建议从配置文件读取不要硬编码。3.4 自然语言待办解析AI 桌宠要能“听懂”用户说的待办事项。最简单的方式是让大模型把自然语言转换成结构化 JSON然后客户端完成解析和存储。# 文件路径desktop_pet/task_parser.py import json TASK_PROMPT 请从用户的输入中提取待办事项。 如果不存在明确的待办事项则返回 {has_task: false}。 如果存在返回如下JSON格式 {has_task: true, title: 待办标题, due_time: YYYY-MM-DD HH:MM 或 null, priority: high/medium/low} 只返回JSON不要包含其他文本。 def parse_task(content): 解析用户输入返回任务字典或 None # 这里可以复用 LLMClient也可以单独请求 reply llm_client.chat(content, system_promptTASK_PROMPT) try: result json.loads(reply) if result.get(has_task): return result except json.JSONDecodeError: pass return None得到结构化任务后可以存储到本地 JSON 文件或 SQLite 中。# 文件路径desktop_pet/storage.py import json import os TASK_FILE tasks.json def save_task(task): tasks load_tasks() tasks.append(task) with open(TASK_FILE, w, encodingutf-8) as f: json.dump(tasks, f, ensure_asciiFalse, indent2) def load_tasks(): if not os.path.exists(TASK_FILE): return [] with open(TASK_FILE, r, encodingutf-8) as f: return json.load(f)这里只做最简单的 JSON 文件存储适合个人桌宠。如果要多人使用建议改用 SQLite 或后端服务。3.5 定时提醒功能提醒是基于时间的任务调度。可以用QTimer来轮询检查任务时间。# 文件路径desktop_pet/timer_service.py from PyQt5.QtCore import QTimer from datetime import datetime class ReminderService: def __init__(self, pet_window, check_interval10000): self.pet_window pet_window self.timer QTimer() self.timer.timeout.connect(self.check_tasks) self.timer.start(check_interval) # 10秒检查一次 def check_tasks(self): now datetime.now().strftime(%Y-%m-%d %H:%M) tasks load_tasks() for task in tasks: due task.get(due_time) if due and due now and not task.get(notified): self.pet_window.show_bubble(f⏰ 提醒{task[title]}) task[notified] True save_tasks(tasks) # 更新状态为了简化这里使用了精确匹配。实际项目中更推荐把时间转换为时间戳比较当前时间戳是否超过了任务时间且未通知。3.6 把 AI 对话接入桌宠交互桌宠怎么发起对话可以提供两种方式点击桌宠弹出一个输入框。监听全局快捷键呼出语音/文字输入。先实现最简单的点击弹出输入框# 文件路径desktop_pet/app.py from PyQt5.QtWidgets import QLineEdit class PetApp(PetWindow): def __init__(self): super().__init__() self.llm LLMClient( api_key你的API_KEY, endpointhttps://your-endpoint/v1/chat/completions, modelqwen-plus ) self.input_box QLineEdit(self) self.input_box.setGeometry(10, -70, 100, 30) self.input_box.setVisible(False) self.input_box.returnPressed.connect(self.on_input) def mouseDoubleClickEvent(self, event): 双击打开输入框 self.input_box.setVisible(True) self.input_box.setFocus() event.accept() def on_input(self): text self.input_box.text().strip() if not text: self.input_box.setVisible(False) return self.show_bubble(思考中...) # 先尝试识别待办 task parse_task(text) if task: save_task(task) self.show_bubble(f已记录待办{task[title]}) else: reply self.llm.chat(text, system_prompt你是一个可爱的桌宠助手回答简洁一点。) self.show_bubble(reply) self.input_box.clear() self.input_box.setVisible(False)这段代码把“对话 待办识别”合并在了一个流程里用户输入一句话先看是不是待办如果是直接保存并反馈否则发给大模型对话。这种设计体验比较流畅。4. 完整项目实战从零搭建一个 AI 桌宠4.1 项目结构为了方便读者复现我整理了一个最小可运行项目结构ai-desktop-pet/ ├── assets/ │ └── pet_default.png # 桌宠素材建议透明背景 PNG ├── desktop_pet/ │ ├── __init__.py │ ├── window.py # 悬浮窗、气泡 │ ├── bubble.py # 气泡显示逻辑可并入 window.py │ ├── llm_client.py # 大模型 API 封装 │ ├── task_parser.py # 待办解析 │ ├── storage.py # 任务存储 │ ├── timer_service.py # 定时提醒 │ └── app.py # 主程序入口 ├── config.json # 配置文件 └── main.py # 启动入口4.2 配置文件{ api_key: sk-xxxxxx, endpoint: https://your-endpoint/v1/chat/completions, model: qwen-plus, check_interval_ms: 10000, window_width: 120, window_height: 120, position: bottom_right }修改完配置后在app.py中读取import json def load_config(): with open(config.json, r, encodingutf-8) as f: return json.load(f)4.3 主入口 main.py# 文件路径main.py import sys from PyQt5.QtWidgets import QApplication from desktop_pet.app import PetApp def main(): app QApplication(sys.argv) pet PetApp() pet.show() sys.exit(app.exec_()) if __name__ __main__: main()4.4 运行与验证在项目根目录执行python main.py预期效果桌面右下角出现一个透明背景的桌宠图片。鼠标左键可拖动。双击后出现输入框输入“下午3点提醒我提交周报”桌宠气泡显示“已记录待办提交周报”。输入“今天天气怎么样”桌宠调用大模型返回回答。到达任务时间后桌宠弹出提醒气泡。4.5 效果演示分析上面的最小闭环已经具备了一个 AI 桌宠的核心体验。但它还不够“效率”——比如没有全局快捷键、没有语音输入、没有调用系统工具。下一步可以按你的需求扩展。5. 进阶功能让桌宠真正“干活”5.1 全局快捷键唤醒用户不可能每次都用鼠标双击桌宠。全局快捷键能帮助提高输入效率对“效率神器”的定位至关重要。可以用pynput或keyboard库实现。这里推荐pynput示例# 文件路径desktop_pet/hotkey.py from pynput import keyboard def start_hotkey_listener(callback, hotkeyctrlspace): def on_activate(): callback() with keyboard.GlobalHotKeys({hotkey: on_activate}) as h: h.join()在PetApp中增加一个toggle_input方法当快捷键按下时切换输入框显示。5.2 语音输入语音输入能把“AI 桌宠”的交互体验提升一个台阶。可以使用SpeechRecognition库配合系统麦克风实现离线或有云转写。# 文件路径desktop_pet/voice_input.py import speech_recognition as sr def listen_once(timeout5): r sr.Recognizer() with sr.Microphone() as source: r.adjust_for_ambient_noise(source) try: audio r.listen(source, timeouttimeout, phrase_time_limit5) except sr.WaitTimeoutError: return try: # 这里可替换为任意在线语音识别服务 text r.recognize_google(audio, languagezh-CN) return text except sr.UnknownValueError: return 注意recognize_google依赖网络且并非所有地区都稳定。生产环境建议使用云厂商的语音识别 API。5.3 调用系统命令真正“效率”的桌宠应该能帮用户打开应用、搜索文件、创建日程。可以把这些能力封装成一组工具函数让大模型通过“工具调用”来选择执行。# 文件路径desktop_pet/tools.py import os import subprocess def open_application(app_name): 根据名称打开常见应用支持 Windows app_map { 记事本: notepad.exe, 计算器: calc.exe, 画图: mspaint.exe, } exe app_map.get(app_name) if exe: subprocess.Popen(exe) return f已打开 {app_name} return 没有找到对应的应用 def get_clipboard(): # 依赖 pyperclip import pyperclip return pyperclip.paste()然后通过提示词让大模型输出 JSON 指令客户端再执行TOOL_PROMPT 如果用户需要打开应用、获取剪贴板等操作返回JSON {action: open_app, params: {app_name: 记事本}} 否则返回 {action: none} def run_tool(reply): try: import json data json.loads(reply) action data.get(action) if action open_app: return open_application(data[params][app_name]) elif action get_clipboard: return get_clipboard() except Exception: return None这个思路本质上是轻量级 AI Agent大模型负责意图理解代码负责执行工具。后续如果你想深入可以学习 ReAct、Function Call 等更规范的 Agent 模式。5.4 多场景自定义你可以把“宠物形象”替换成任何一组动画帧或者接入 Live2D 引擎。但要注意Live2D 通常需要额外的渲染组件如果完全用 PyQt 实现建议直接使用动画 GIF 或逐帧切换图片。6. 常见问题与排查思路6.1 桌宠窗口不能透明问题现象常见原因解决思路窗口背景是黑色或白色没有设置WA_TranslucentBackground检查setAttribute是否在show()之前调用透明设置无效部分 Linux 环境需要开启合成器换 Windows 测试或安装picom6.2 拖动时窗口闪烁拖动时如果频繁重绘可能导致闪烁。解决方法更新位置时不要改变尺寸。使用move()而不是setGeometry()。减少透明回调和阴影效果。6.3 AI 接口超时大模型 API 有时响应较慢。主要是超时时间设置太短或网络不稳定。解决建议将timeout从 30 秒提高到 60 秒。增加重试机制最多重试 2 次。在请求前先显示“思考中”气泡避免用户重复点击。6.4 待办解析失败如果大模型没有返回标准 JSON表现可能是“有回复但没有记录任务”。排查步骤打印llm_client.chat的原始返回内容确认是否为合法 JSON。检查TASK_PROMPT是否足够清晰。在parse_task中使用json.loads(reply.strip())并去除异常字符。如果模型经常返回 markdown 代码块如json\n{...}\n需要先剥离 标记。示例修复import re def clean_json(reply): match re.search(r\{.*\}, reply, re.S) if match: return match.group() return reply6.5 提醒不触发原因通常是时间格式不匹配或任务已经标记了notified。检查任务due_time是否与datetime.now().strftime(%Y-%m-%d %H:%M)的格式一致。check_tasks是否在运行确认QTimer被实例化。跨天提醒需要处理日期部分单纯比较字符串在跨年时会失效。7. 最佳实践与工程建议7.1 架构上区分“本地客户端”和“AI 服务”不要把所有逻辑都塞进可视化窗口类里。推荐分层UI 层窗口、动画、交互 ↓ 业务层待办管理、提醒、工具调用 ↓ 服务层LLM 客户端、语音识别、本地存储这样后续把桌宠迁移到 Tauri或把 AI 服务部署到云端都能平滑切换。7.2 配置管理要安全不要把 API Key 直接写在代码中至少使用config.json并在.gitignore中忽略。生产环境使用环境变量。import os api_key os.getenv(PET_API_KEY, config.get(api_key))7.3 对话上下文要控制每次对话都携带全部历史会导致 token 爆炸而且响应变慢。建议只保留最近 10 轮。如果单轮超过 2000 字丢弃最旧内容。待办解析不要复用日常对话的历史直接使用独立的 Prompt。7.4 异常处理要完整大模型接口、网络、文件读写都可能失败。桌宠是长时间运行的桌面程序任何未捕获的异常都会导致进程退出。建议在chat方法内部捕获所有异常。在check_tasks中使用try-except。主程序中增加全局异常钩子把错误写入日志文件。import logging logging.basicConfig( filenamepet.log, levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, ) def global_exception_hook(exc_type, exc_value, exc_tb): logging.error(Unhandled exception, exc_info(exc_type, exc_value, exc_tb)) sys.excepthook global_exception_hook7.5 性能优化桌宠长期驻留内存要注意资源占用不要频繁刷新 UI气泡显示使用一次性定时器。轮询提醒间隔建议 10 秒以上避免无意义的 CPU 损耗。大模型请求放在子线程里避免阻塞 UI 线程。PyQt 的 UI 操作必须在主线程因此子线程返回后要通过信号槽更新气泡。示例用QThread或threading QTimer实现。简单情况下可以使用QThreadPool但要注意回调方式。7.6 安全边界桌面工具具备执行命令的能力后一定要有安全设计不要把任意自然语言直接拼接到os.system。工具白名单机制只允许预设的应用或命令。涉及文件删除、网络修改等敏感操作需要二次确认。测试环境使用虚拟目录或沙箱再在生产环境使用。8. 总结与下一步学习方向这次实战打通了一个 AI 桌宠的完整链路PyQt5 实现桌面悬浮和交互大模型完成对话理解任务系统做到待办提取与定时提醒工具调用让桌宠具备了“动手”能力。整条路线并不复杂真正有价值的是把 AI Agent 的“意图理解 工具执行”落地在一个看得见摸得着的桌面上。如果你想让这个项目继续进化可以从这几个方向深入接入 Function Calling让大模型原生支持工具调用而不是靠 JSON 提示词解析。接入语音输入输出打造“无手操作”体验。使用 Tauri 重构客户端把安装包体积降到 10MB 以内。把待办同步到日历或任务管理工具例如 WebDAV、CalDAV、团队协作 API。加上表情与状态机根据用户的工作节奏动态切换桌宠情绪。最后提醒一句桌宠是“陪伴型工具”不是“监控工具”。在使用摄像头、麦克风、读取屏幕等能力时务必遵守隐私规范和用户授权。开发时注意本地数据保护不给用户带来安全风险。动手把代码跑起来再按照自己的需求改一版。做一个专属你的打工人解压 AI 效率小助手其实并没有想象中那么难。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →