# 颜文字桌宠 **Repository Path**: sutanm/ai-pet ## Basic Information - **Project Name**: 颜文字桌宠 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-05 - **Last Updated**: 2026-06-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI桌宠 - 智能桌面伴侣 🐾 基于 DeepSeek 大模型的智能桌面宠物应用,提供拟人化交互体验。具有透明无边框窗口、气泡对话、情绪识别、长期记忆等功能。 ## ✨ 核心特性 - ✅ DeepSeek 流式对话,逐字回复,实时显示 - ✅ 桌面气泡弹出,自动淡出消失,支持停留时间配置 - ✅ AI 情绪识别 + 颜文字表情联动(开心、思考、害羞等 11 种情绪) - ✅ 轻面板快捷输入 / 大气泡对话框双模式交互 - ✅ **对话打断** — 发送中可中断 AI 回复,立即开始新对话 - ✅ **对话历史面板** — 新建/切换/删除对话,管理多轮会话 - ✅ **跨对话记忆** — 新对话也能检索全部历史 + 提取的事实 - ✅ **颜文字配色** — 自定义颜色和字号,实时预览 - ✅ **桌面模式** — 类似聊天软件的完整界面,支持侧边栏对话管理 - ✅ 配置面板自适应卡片网格,可拖拽调整大小 - ✅ 系统托盘常驻,关闭不退出,后台运行 - ✅ 透明无边框窗口,可拖拽移动,边缘吸附功能 - ✅ **宠物属性引擎** — 心情/精力/饱腹/好感实时变化,影响闲置动画 - ✅ **宠物技能系统** — `/pet feed` 喂食、AI 标签 `[SKILL: play]` 触发,用户可自定义 `skills.json` - ✅ **自主闲逛** — 史莱姆弹跳式窗口移动,贴边/对话中自动暂停 - ✅ **桌面窗口边缘拖拽** — 透明把手 + 手动 resize,最小化/最大化/还原按钮 ## 快速开始 ### 1. 环境准备 确保已安装 Python 3.9+,然后创建虚拟环境: ```bash # 创建虚拟环境(Windows) python -m venv .venv .venv\Scripts\activate # 创建虚拟环境(macOS/Linux) python3 -m venv .venv source .venv/bin/activate ``` ### 2. 安装依赖 ```bash pip install -r requirements.txt ``` ### 3. 配置 API 密钥 编辑 `.env` 文件,设置 DeepSeek API 密钥: ```bash DEEPSEEK_API_KEY=sk-your-api-key-here ``` 获取 API 密钥:[DeepSeek 开放平台](https://platform.deepseek.com) > **注意**:API 密钥应放在 `.env` 文件中,而不是 `config.toml`。 ### 4. 运行程序 ```bash python main.py # 或者使用脚本入口(如果已安装) ai-pet ``` ## 📖 使用说明 ### 基本操作 | 操作 | 方式 | |------|------| | 移动位置 | 左键按住颜文字拖拽,靠近屏幕边缘自动吸附 | | 快捷输入 | 左键点击颜文字 → 轻面板快速对话 | | 打开对话 | 双击颜文字 / 托盘图标 → 完整气泡对话框 | | 打断 AI | 发送中点击 `■ 打断` 按钮 → 立即停止 + 保存部分内容 | | 宠物技能 | 输入 `/pet feed` 喂食、`/pet play` 玩耍、`/pet pet` 抚摸等 6 种技能 | | 历史对话 | 轻面板 `💬 历史` → 新建/切换/删除/清空对话 | | 桌面模式 | 轻面板 `🖥 桌面` 或托盘菜单 → 切换到桌面模式 | | 发送消息 | 输入框输入,回车或点击发送按钮 | | 颜色配置 | 右键菜单 → 配置 → 🎨 颜文字预览区 → 选色/字号 | | 退出程序 | 菜单选择"退出"(关闭窗口仅最小化到托盘) | ### 交互流程 1. 输入框发送消息 2. 颜文字切换为"思考"状态,显示动画 3. AI 流式回复,桌面弹出气泡(逐句显示) 4. 对话对话框同步显示完整对话历史 5. 气泡自动淡出消失(停留时间可配置) 6. 情绪识别触发颜文字表情变化 ### 桌面模式 通过轻面板 `🖥 桌面` 按钮或托盘菜单进入桌面模式: - **标题栏** — 自定义无边框标题栏:◀(侧栏) + 标题 + [—] [□] [✕] - `—` = 最小化到任务栏 - `□`/`❐` = 最大化 / 还原,图标自动切换 - `✕` = 返回桌宠模式(非退出) - **窗口边缘** — 8 个透明把手(四边 5px + 四角 10×10px),带内侧 1px 半透明边界线,拖拽调整窗口大小 - **工具栏列** (44px) — 始终可见,底部有 ⚙ 配置按钮 - **侧边栏** — 嵌入的颜文字桌宠(闲置动画活跃) → 新建对话 → 对话列表(悬停 🗑) → 重命名/删除/清空 - **聊天面板** — 消息气泡(用户:灰色,AI:白色)+ 居中输入栏带 ↑ 按钮 - **输出路由** — StreamResponse 根据 `_output_mode` 路由输出:`"pet"` → BubbleDialog+BubbleQueue, `"desktop"` → ChatPanel ### 对话历史面板 点击轻面板 `💬 历史` 按钮打开: - **新建对话** — 创建全新的干净上下文 - **切换到此** — 回到之前的某个对话继续聊 - **删除此** — 删除选中的对话及其所有消息 - **清空全部** — 清除所有对话记录(不可撤销) ### 跨对话记忆 开启后(默认),新建对话时 AI 依然能"记得"你: - 从所有历史对话中检索相关语义片段 - 加载以前提取的用户事实(偏好、习惯等) - 自动注入声明行,防止 AI 混淆当前/过去信息 - 可在配置面板 `🧠 跨对话记忆` 卡片中关闭 ### 高级功能:记忆系统 AI桌宠具备长期记忆能力,能够记住用户的重要信息和历史对话: - **对话摘要**:自动总结长对话,保留关键信息 - **事实提取**:从对话中提取用户偏好、习惯等事实 - **语义搜索**:基于向量相似度检索相关历史对话 - **预算管理**:智能控制上下文长度,优化Token使用 ### 配置选项 右键菜单 → "配置" 打开设置对话框(可拖拽缩放): #### 🔌 API 连接 - **API Key**:DeepSeek API 密钥(从 `.env` 读取) - **HF Token**:Hugging Face Token(模型下载) - **模型**:使用的 AI 模型名称 #### 🔍 搜索设置 - **SearXNG 地址**:自建搜索引擎(留空仅用 Bing) - **引擎**:指定搜索引擎列表 - **抓取页数**:自动抓取搜索结果页面的数量 #### 🖥 桌面外观 - **窗口尺寸**:迷你 / 小 / 中 / 大 - **气泡停留时间**:1000–30000 毫秒 - **托盘通知**:是否启用托盘消息提醒 #### ⚙ 高级选项 - **打字机效果**:气泡逐字弹出 + 速度调节 - **启动时打开对话框**:程序启动即显示对话窗口 - **编辑 Prompt / 表情包**:直接打开系统文件编辑 #### 🎨 颜文字预览 - **颜色**:点击色块打开取色器,预览区实时反映 - **字号**:拖动滑块 28–70px,预览区同步变化 - **换一个看看**:随机切换预览的颜文字 #### 🧠 跨对话记忆 - **启用开关**:新对话检索全部历史 + 提取的事实 - 关闭后仅搜索当前对话 #### 🖥 桌面模式 - **切换方式**:轻面板 `🖥 桌面` 按钮或托盘菜单 - **功能特点**:类似聊天软件的完整界面,支持侧边栏对话管理 - **返回方式**:点击标题栏 ✕ 按钮返回桌宠模式 ## 项目结构 ``` ai_pet/ ├── main.py # 程序入口 ├── config.toml # 应用配置(gitignored) ├── requirements.txt # Python 依赖 │ ├── app/ # 主应用包 │ ├── config.py # 配置单例 │ ├── ai/ # AI 模块 │ │ ├── client.py # DeepSeek API 客户端 │ │ ├── conversation.py # 对话历史管理 + 系统提示词 │ │ ├── chatmemory.py # 聊天记忆 + 对话 CRUD │ │ ├── memory_cleanup.py # 记忆清理(TTL + Token 预算驱逐) │ │ ├── vector_store.py # ChromaDB 向量存储 │ │ ├── token_utils.py # Token 估算工具 │ │ ├── fact_manager.py # 用户事实提取 │ │ ├── summary_manager.py # 对话摘要管理 │ │ ├── protocol_text.py # 协议指令文本 │ │ └── tools/ # AI 工具 │ │ ├── base.py # 工具基类 │ │ ├── registry.py # 工具注册表 │ │ ├── builtins.py # 内置工具 │ │ └── search.py # Bing/SearXNG 搜索 + 页面抓取 │ ├── window/ # 窗口模块 │ │ ├── main_window.py # 主窗口(透明无边框) │ │ ├── theme.py # 主题管理器(QSS) │ │ ├── dialogs/ # 对话框 │ │ │ ├── bubble_dialog.py # 气泡聊天对话框 │ │ │ ├── config_dialog.py # 配置面板 │ │ │ └── config_cards.py # 配置卡片构建器 │ │ ├── managers/ # 管理器 │ │ │ ├── bubble_queue.py # 气泡队列 + 生命周期 │ │ │ ├── stream_response.py # 流式打字机 + 标签解析 │ │ │ ├── markdown_splitter.py # 分句器 │ │ │ ├── pet_status.py # 宠物属性引擎 │ │ │ ├── pet_skills.py # 宠物技能系统 │ │ │ ├── pet_wander.py # 自主闲逛(弹跳移动) │ │ │ ├── desktop_mode.py # 桌面模式 mixin │ │ │ ├── window_drag.py # 窗口拖拽 + 贴边 │ │ │ └── window_lifecycle.py # 窗口生命周期 │ │ ├── widgets/ # 组件 │ │ │ ├── pet_widget.py # 颜文字桌宠本体 │ │ │ ├── pet_edge.py # 贴边吸附 │ │ │ ├── quick_panel.py # 快捷输入面板 │ │ │ ├── conversation_panel.py# 历史对话管理 │ │ │ ├── conv_operations.py # 批量删除操作 │ │ │ ├── reply_bubble.py # 桌面回复气泡 │ │ │ ├── animations.py # 桌宠动画 │ │ │ ├── desktop_sidebar.py # 桌面模式侧边栏 │ │ │ └── chat_panel.py # 桌面模式聊天面板 │ │ └── workers/ # 工作线程 │ │ └── chat_worker.py # AI 流式请求线程(可中断) │ ├── resources/ │ │ └── themes/ # 主题样式 │ │ ├── _colors.json # 颜色变量 │ │ ├── pet.qss # 桌宠模式样式 │ │ └── desktop.qss # 桌面模式样式 │ └── utils/ # 工具 │ ├── kaomoji_sanitize.py # 颜文字 emoji 过滤 │ └── logger.py # 日志模块(Win安全轮转) │ ├── resources/ # 静态资源 │ ├── emoticons.json # 表情情绪定义 │ ├── animations.json # 闲置动画组 + 权重矩阵 │ ├── skills.json # 宠物技能定义 │ └── system_prompt.txt # 自定义系统提示词 │ ├── scripts/ # 工具脚本 │ └── clear_database.py # 清理所有数据 │ └── tests/ # 测试 ├── conftest.py # pytest 配置 ├── test_config.py # 配置测试 ├── test_conversation.py # 对话测试 ├── test_memory_layer.py # 记忆层测试 └── test_token_utils.py # Token 工具测试 ``` ## 自定义 ### 修改颜文字 编辑 `resources/emoticons.json`,按情绪分类添加: ```json { "表情": { "开心": ["(゚∀゚)", "(◕‿◕)"], "愤怒": ["(╯°Д°)╯ ┻━┻"] }, "special": { "thinking": ["♒((⇀‸↼))♒"] } } ``` ### 自定义闲置动画 编辑 `resources/animations.json`: ```json { "idle_groups": { "blink": ["(・ω・)", "(-ω-)", "(・ω・)"], "wiggle": ["(ノ◕ヮ◕)ノ", "(ノ◕ヮ◕)ノ*"] }, "idle_weights": { "very_happy": { "active": {"wiggle":5, "bounce":4, "sparkle":3}, "tired": {"smile":8, "sleepy":6, "blink":4} } } } ``` - `idle_groups`:按组名定义帧序列 - `idle_weights`:5 种心情 × 3 种精力 = 15 组权重,每组内按权重随机选动画组 ### 自定义宠物技能 编辑 `resources/skills.json`: ```json { "feed": { "effects": {"hunger": 30, "affection": 5}, "action": "bounce", "kaomoji": "(・ω・)🍰", "feedback": "好吃!" } } ``` - `effects`:对 mood/energy/hunger/affection 的增减量 - `action`:动画类型(bounce/shake/wave/none) - `kaomoji`:技能播放时显示的颜文字 - `feedback`:执行后返回的描述文字 触发方式: - **用户命令**:在输入框输入 `/pet feed` 直接执行 - **AI 标签**:AI 回复中通过 `[SKILL: feed]` 标签触发 ### 自定义系统提示词 编辑 `resources/system_prompt.txt`,修改后重新打开配置即可生效。 ### 配置文件 `config.toml` 支持以下配置选项: ```toml [app] name = "AI Pet" version = "1.0.0" [window] size = "medium" # mini | small | medium | large auto_open_dialog = false # 启动时是否自动打开对话框 bubble_hold_time = 5000 # 气泡停留时间(毫秒) [ai] provider = "deepseek" model = "deepseek-chat" base_url = "https://api.deepseek.com" # API 密钥应放在 .env 文件中,不要写在这里! [pet] color = "#FF6B9D" # 颜文字文字颜色 font_size = 28 # 桌宠字体大小 auto_care = false # 自动照顾(冻结属性衰减) wander_enabled = true # 自主闲逛 dnd_enabled = false # 免打扰模式 [desktop] hide_pet = false # 桌面模式隐藏侧边栏桌宠 pet_font_size = 28 # 桌面模式桌宠字体大小 [bubble] typewriter = false # 是否启用打字机效果 typewriter_speed = 30 # 打字机速度(毫秒/字符) [memory] max_context_tokens = 8000 # 最大上下文 Token 数 reserve_tokens = 1000 # 预留 Token 空间 cross_conversation_search = true # 跨对话记忆搜索 semantic_max_results = 2 # 语义搜索最大结果数 semantic_score_threshold = 0.55 # 语义搜索分数阈值 summary_max_in_context = 2 # 上下文中最大摘要数 fact_max_in_context = 3 # 上下文中最大事实数 max_total_messages = 500 # 全局最大消息数 max_messages_per_conv = 30 # 每会话最大消息数 min_keep_recent = 10 # 最少保留最近消息数 summarize_before_delete = true # 删除前是否先摘要 fact_extraction_interval = 16 # 事实提取触发消息数 [storage] enable_vector_search = true # 启用向量搜索 message_ttl_days = 7 # 消息 TTL 天数 [tools] enabled = true # 是否启用搜索工具 searxng_url = "" # SearXNG URL(留空仅用 Bing) max_content_pages = 5 # 最大抓取页数 [system] tray_notification_enabled = true # 是否启用托盘通知 hotkey_quick_panel = "F1" # 快捷面板快捷键 ``` > **重要提示**:`config.toml` 和 `.env` 都在 `.gitignore` 中,不会被提交到版本控制。 ## 🛠️ 技术栈 - **Python 3.9+** — 编程语言 - **PyQt6** — GUI 框架,透明窗口、信号槽机制 - **OpenAI SDK** — LLM API 调用(兼容 DeepSeek) - **ChromaDB** — 向量数据库,语义搜索和长期记忆 - **SQLite** — 关系型数据库,对话历史存储 - **sentence-transformers** — 文本嵌入模型(paraphrase-multilingual-MiniLM-L12-v2) - **python-dotenv** — 环境变量管理 - **QThread** — 异步流式处理,避免界面卡顿 - **httpx / curl_cffi** — HTTP 请求库,支持浏览器指纹模拟 - **BeautifulSoup4** — HTML 解析,网页内容提取 - **toml / tomllib** — TOML 配置文件读写 ## 💡 核心功能实现方案 ### 1. 透明无边框窗口 **实现方式**: - 使用 `Qt.WindowType.FramelessWindowHint` 移除系统边框 - `Qt.WidgetAttribute.WA_TranslucentBackground` 启用透明背景 - `QPainter` 绘制 1% 不透明度背景确保窗口可命中 - 自定义拖拽逻辑:记录鼠标偏移量,实时更新窗口位置 - 边缘吸附:检测屏幕边界距离 <50px 时自动贴边 **关键文件**:`app/window/main_window.py`, `app/window/managers/window_drag.py` ### 2. 颜文字桌宠显示 **实现方式**: - `QLabel` 显示颜文字,使用等宽字体 `NSimSun` 保证对齐 - 从 `resources/emoticons.json` 加载表情库,按情绪分类 - **闲置动画状态机**:加权选组 → 帧循环(500ms/帧) → 停顿(1.5-4s) → 换组,12 组 × 15 种心情/精力权重矩阵 - 配置颜色通过 `QSS` 动态应用:`QLabel { color: #FF6B9D; }` - 动画通过 `QPropertyAnimation` 实现跳跃/摇晃/弹跳/挥手(仅位置动画,不控制脸) - 桌面模式下闲置动画不停止,嵌入侧边栏继续运行 **关键文件**:`app/window/widgets/pet_widget.py`, `app/window/widgets/animations.py`, `resources/animations.json` ### 3. 流式对话与打字机效果 **实现方式**: - **协议格式**:AI 回复必须遵循 `[EMOTION][KAOMOJI][ACTION][TEXT_START]... [TEXT_END]` 标签格式 - **逐字输出**:`StreamResponse` 接收字符流,通过 `QTimer` 逐字刷入 QTextDocument - **Markdown 分句**:`MarkdownSplitter` 按行分割,长句在 `,;` 处二次拆分 - **气泡队列**:`BubbleQueue` 维护最多 3 个并发气泡,先进先出 - **两种模式**: - 整句模式:累积完整句子后一次性弹出气泡(默认) - 打字机模式:逐字追加到当前气泡,实时调整高度 **关键文件**:`app/window/managers/stream_response.py`, `app/window/managers/bubble_queue.py`, `app/window/managers/markdown_splitter.py` ### 4. 气泡生命周期管理 **实现方式**: - `ReplyBubble` 继承 `QWidget`,使用 `QPainterPath` 绘制圆角矩形 + 三角形尾巴 - **淡入动画**:`QPropertyAnimation` 控制 `windowOpacity` 从 0→1(300ms) - **停留计时**:`QTimer.singleShot` 触发淡出,流式中缩短为 1.5s - **淡出动画**:opacity 从 1→0(350ms),完成后 `deleteLater()` 销毁 - **堆叠布局**:新气泡从底部弹出,旧气泡向上平滑移动(防抖合并多次布局) - **免打扰模式**:配置开关禁用所有气泡弹出 **关键文件**:`app/window/widgets/reply_bubble.py`, `app/window/managers/bubble_queue.py` ### 5. 情绪识别与颜文字联动 **实现方式**: - AI 在回复中声明 `[EMOTION: 开心]` 和 `[KAOMOJI: (≧▽≦)]` - `StreamResponse._handle_tag()` 解析标签,映射到预定义情绪列表 - 调用 `PetWidget.set_emotion()` 切换颜文字,停止空闲轮换 - 2 秒后自动恢复空闲状态(`_restore_timer`) - 动画动作通过 `PetAnimator.play_animation()` 播放 QPropertyAnimation **关键文件**:`app/window/managers/stream_response.py`, `app/window/widgets/pet_widget.py` ### 6. 对话打断机制 **实现方式**: - 用户点击「■ 打断」按钮触发 `MainWindow.interrupt()` - **断开信号**:disconnect worker 的 response_signal/error_signal - **请求中断**:`worker.requestInterruption()` + 2 秒超时强制 terminate - **保存部分**:`StreamResponse.abort()` 将已接收内容存入数据库 - **清理状态**:清空气泡队列、重置颜文字为空闲、取消发送状态 **关键文件**:`app/window/main_window.py:522-559` ### 7. 对话历史管理 **实现方式**: - **SQLite 存储**:`conversations` 表存会话元数据,`messages` 表存消息 - **自动标题**:首条用户消息前 15 字符作为对话标题 - **CRUD 操作**:`ChatMemory` 提供 new/switch/delete/list/update_conversation - **面板交互**:`ConversationPanel` 显示列表,悬停显示删除按钮 - **批量操作**:支持删除单个/清空全部/重命名对话 **关键文件**:`app/ai/chatmemory.py`, `app/window/widgets/conversation_panel.py` ### 8. 跨对话记忆系统 **实现方式**: - **三层记忆架构**: 1. **固定保护区**(Pinned):协议指令 + 角色卡,永不淘汰 2. **条件注入区**(Conditional):事实提取 + 对话摘要 + 语义检索 3. **动态上下文**(Dynamic):最近 N 条消息,Token 预算控制 - **事实提取**:`FactManager` 后台线程从用户消息提取偏好/习惯(~16 条消息触发) - **对话摘要**:`SummaryManager` 在消息达到阈值时生成摘要(首次 12 条,增量 8 条) - **语义检索**:`VectorStore` 使用 ChromaDB + MiniLM 模型,搜索相似度 >0.55 的历史片段 - **跨对话开关**:`cross_conversation_search=true` 时检索所有对话,否则仅限当前 - **免责声明**:注入 `【跨对话记忆】` 提示 AI 区分当前/历史信息 **关键文件**:`app/ai/chatmemory.py`, `app/ai/fact_manager.py`, `app/ai/summary_manager.py`, `app/ai/vector_store.py` ### 9. Token 预算管理 **实现方式**: - **总预算**:`max_context_tokens=8000`,预留 `reserve_tokens=1000` 给 AI 回复 - **优先级装配**: 1. Pinned messages(协议 + 角色卡) 2. 时间戳 system message 3. 协议提醒(漂移检测:连续 2 轮不合规触发紧急恢复) 4. 跨对话声明(可选) 5. 事实提取(最多 3 条) 6. 对话摘要(最多 2 条) 7. 语义检索结果(最多 2 条,阈值 0.55) 8. 最近消息(倒序填充,跳过已摘要的消息 ID) - **估算工具**:`estimate_tokens()` 按 UTF-8 字节数 /4 粗略估算 **关键文件**:`app/ai/chatmemory.py:207-277`, `app/ai/token_utils.py` ### 10. 桌面模式 **实现方式**: - **单窗口双布局**:`MainWindow` 继承多个 Mixin,动态切换中央部件 - **Pet 模式**:仅显示 PetWidget,对话框/轻面板浮窗弹出 - **Desktop 模式**: - 自定义标题栏(◀侧栏 + 标题 + —最小化 + □最大化/还原 + ✕返回宠模式) - 左侧工具栏列(44px,⚙ 配置按钮) - `QSplitter` 分割侧边栏和聊天面板 - `DesktopSidebar`:嵌入 PetWidget(闲置动画活跃) + 对话列表 + 新建按钮 - `ChatPanel`:消息气泡列表 + 居中输入栏 - **窗口边缘调整**:8 个透明 `_ResizeGrip` QWidget(5px 边 + 10×10px 角),内侧 1px 半透明边界线,`grabMouse` + `setGeometry` 驱动 resize - **输出路由**:`StreamResponse._output_mode` 决定输出目标(pet→BubbleDialog, desktop→ChatPanel) - **按钮图标**:最小化/最大化/关闭使用 `QStyle.standardIcon()` 原生 Windows 图标 **关键文件**:`app/window/main_window.py`, `app/window/managers/desktop_mode.py`, `app/window/widgets/desktop_sidebar.py`, `app/window/widgets/chat_panel.py` ### 11. 宠物属性引擎 **实现方式**: - **4 属性**:mood(心情)、energy(精力)、hunger(饱腹)、affection(好感),范围 0-100 - **自动衰减**:每 30 秒按设定速率衰减(心情 0.5、精力 0.3、饱腹 0.4、好感 0.1 / 0.5min) - **auto_care 冻结**:开关开启时属性锁定在初始高位,不衰减 - **属性分级**: - mood:very_happy(≥80) / happy(≥60) / neutral(≥40) / sad(≥20) / very_sad - energy:active(≥70) / normal(≥30) / tired - hunger:full(≥70) / normal(≥30) / hungry - **联动闲置动画**:mood × energy 决定动画组权重矩阵(12 组 × 15 种状态组合) **关键文件**:`app/window/managers/pet_status.py` ### 12. 宠物技能系统 **实现方式**: - **6 种技能**:feed(喂食)、play(玩耍)、pet(抚摸)、rest(休息)、praise(夸奖)、scold(责备) - **定义文件**:`resources/skills.json`,用户可直接编辑自定义 - **双路径触发**: - 用户 `/pet feed` → `MainWindow.send_message()` 拦截,直接执行不发送 AI - AI `[SKILL: play]` 标签 → `StreamResponse._handle_tag()` 即时执行,不入 pending 队列 - **效果**:更新属性值 → 播放动画 → 显示 kaomoji → 返回 feedback 文字 - **协议注入**:`ConversationManager.get_pinned_messages()` 动态生成技能描述注入系统提示词 **关键文件**:`app/window/managers/pet_skills.py`, `resources/skills.json` ### 13. 自主闲逛 **实现方式**: - **Q 弹移动**:QTimer 16ms 逐帧 `move()`,水平线性 + 垂直抛物线弹跳(`y = y_linear - HEIGHT × 4t(1-t)`) - **随机参数**:间隔 3-8s、每序列 2-5 跳、距离 80-200px、8 方向随机 - **自动暂停**:贴边/对话中/QuickPanel 打开/DnD 模式/低精力(<30)时自动停止 - **配置开关**:`pet_wander_enabled` 控制全局启用 **关键文件**:`app/window/managers/pet_wander.py` ### 14. 网络搜索工具 **实现方式**: - **双引擎**:优先 SearXNG(自建聚合搜索),降级 Bing - **SearXNG 连通性检查**:`_SearXNGCheckWorker` 后台链式降级 `bing→baidu→360search`,真实 query `q=test`,验证 `len(results)>0`,3s 超时 - **SearXNG**:HTTP GET `/search?format=json&q=...`,按 score 排序 - **Bing**:解析 `li.b_algo` 提取标题/链接/摘要 - **页面抓取**: - `curl_cffi` 模拟 Chrome 131 浏览器指纹(绕过反爬) - `BeautifulSoup` 剥离 script/style/nav/footer 等噪音标签 - 多线程并发抓取(最多 4 线程,随机延迟 0.2-0.6s) - 质量过滤:平均行长 <60 且短行占比 >75% 视为低质,回退到 snippet - **Token 限制**:搜索结果总 Token <1500,单页内容 <2000 - **防过载**:单次对话最多 10 条搜索结果,达到后停止工具调用 **关键文件**:`app/ai/tools/search.py`, `app/ai/tools/builtins.py`, `app/ai/tools/registry.py` ### 15. 配置系统 **实现方式**: - **单例模式**:`AppConfig.instance()` 线程安全懒加载(双重检查锁) - **TOML 读写**:Python 3.11+ 用 `tomllib`(只读),写操作用 `toml` 库 - **属性访问**:通过 `@property` 暴露配置项,带默认值 - **热重载**:`ConfigDialog` 保存后调用 `AppConfig.reload()` 重新读取磁盘 - **Git 忽略**:`config.toml` 和 `.env` 均在 `.gitignore` 中 **关键文件**:`app/config.py`, `app/window/dialogs/config_dialog.py` ### 16. 系统托盘常驻 **实现方式**: - `QSystemTrayIcon` 创建托盘图标,使用 QPainter 绘制颜文字 QPixmap - **关闭行为**:重写 `closeEvent()` 忽略关闭事件,调用 `hide()` 隐藏窗口 - **退出方式**:托盘菜单「退出」或 `QApplication.quit()` - **崩溃清理**:`atexit.register(_cleanup)` 确保托盘图标被隐藏 - **通知系统**:`show_tray_notification()` 根据配置决定是否显示(支持免打扰) **关键文件**:`app/window/main_window.py:281-336`, `main.py:24-34` ### 17. 资源清理与退出安全 **实现方式**: - **显式析构**:`main.py` 在 event loop 结束后手动 `del window` 和 `del app` - **组件清理**: - `PetWidget.cleanup()`:停止所有 QTimer(空闲轮换/情绪恢复/搜索循环/悬停延迟) - `ReplyBubble.cleanup()`:停止淡入/淡出/移动动画和停留计时器 - `ChatMemory.cleanup()`:关闭 SQLite 连接、shutdown ThreadPoolExecutor、清理 ChromaDB - **异常捕获**:所有清理操作包裹 try-except,避免二次崩溃 - **日志记录**:每个清理步骤记录 debug/error 日志便于排查 **关键文件**:`main.py:74-99`, `app/window/widgets/pet_widget.py:369-384`, `app/ai/chatmemory.py:462-486` ### 18. 快捷键支持 **实现方式**: - `QShortcut` 注册全局快捷键(`ApplicationShortcut` 上下文) - 默认 `F1` 唤起轻面板并聚焦输入框 - 配置项 `hotkey_quick_panel` 可在 config.toml 中自定义 **关键文件**:`app/window/main_window.py:203-217` ### 19. 主题系统 **实现方式**: - `ThemeManager` 单例加载 `resources/themes/pet.qss` 和 `desktop.qss` - QSS 样式表应用到 QWidget,支持对象名选择器(如 `#QuickPanelBody`) - 配置更改后调用 `style().unpolish()` + `polish()` 强制刷新 **关键文件**:`app/window/theme.py`, `app/resources/themes/pet.qss` ## ❓ 常见问题 **Q: 窗口没有边框,怎么移动?** A: 左键按住颜文字直接拖拽,靠近屏幕边缘会自动吸附。 **Q: 如何打开对话框?** A: 双击颜文字,或双击托盘图标,或左键点击打开轻面板再点“对话”。 **Q: 关闭窗口后程序退出了?** A: 关闭按钮仅最小化到托盘,右键托盘或菜单中选择“退出”才是完全退出。 **Q: 颜文字显示为方框?** A: 确保系统安装支持 Unicode 的等宽字体(如 NSimSun、Microsoft YaHei)。 **Q: API 调用失败,提示认证错误?** A: 检查 `.env` 文件中的 `DEEPSEEK_API_KEY` 是否正确,确保使用完整的 API 密钥字符串,不要截断。 **Q: 如何清理所有数据重新开始?** A: 运行 `python scripts/clear_all_data.py` 清除数据库和向量存储。 **Q: 记忆功能不工作?** A: 检查配置对话框中的记忆设置,确保启用了相关功能。首次启动可能需要一些对话来建立记忆。 **Q: 如何打断 AI 的长回复?** A: 点击轻面板的 ■ 打断 按钮(发送中自动切换),已接收的部分内容会保存。 **Q: 新建对话后 AI 还会记得我吗?** A: 默认开启跨对话记忆,AI 会从全部历史中检索相关信息。可在配置面板中关闭。 **Q: 如何管理多个对话?** A: 点击轻面板 💬 历史 打开对话管理面板,可新建/切换/删除对话。 **Q: 如何更改颜文字颜色?** A: 右键菜单 → 配置 → 预览区 → 点击色块选择颜色,实时预览后保存。 **Q: 气泡停留时间太短/太长?** A: 右键菜单 → 配置 → 桌面外观 → 调整气泡停留时间(1000-30000 毫秒)。 **Q: 什么是桌面模式?** A: 桌面模式是一个类似聊天软件的完整界面,包含侧边栏对话管理和聊天面板。可通过轻面板 `🖥 桌面` 按钮或托盘菜单进入。 **Q: 桌面模式怎么调整窗口大小?** A: 窗口四边有透明把手(5px 宽),四角有 10×10px 把手,拖拽即可调整。内侧有 1px 半透明边界线辅助识别窗口边缘。 **Q: 宠物有哪些技能?怎么用?** A: 6 种技能:`/pet feed` 喂食、`/pet play` 玩耍、`/pet pet` 抚摸、`/pet rest` 休息、`/pet praise` 夸奖、`/pet scold` 责备。输入框直接输入命令执行,AI 也会通过标签自动触发。技能定义可编辑 `resources/skills.json`。 **Q: 宠物的心情/精力是怎么变化的?** A: 每 30 秒自动衰减,心情 -0.5、精力 -0.3、饱腹 -0.4、好感 -0.1(每 0.5 分钟等效)。使用技能可以恢复。配置面板中可开启"自动照顾"冻结衰减。 **Q: 桌宠为什么会在桌面自己动?** A: 自主闲逛功能,随机方向和距离进行史莱姆弹跳式移动。贴边、对话中、免打扰模式、低精力时会自动暂停。可在配置中关闭。 ## 🧪 测试 运行所有测试: ```bash pytest ``` 运行单个测试文件: ```bash pytest tests/test_config.py pytest tests/test_memory_layer.py ``` 运行单个测试用例: ```bash pytest tests/test_config.py::test_singleton_identity ``` > **注意**:ChromaDB 有后台线程,测试使用临时目录并在清理时自动销毁。 ## 📝 开发规范 ### 代码质量 ```bash # Lint 检查(ruff) ruff check . # 类型检查(mypy) mypy app/ ``` ### 导入顺序 使用 ruff isort,配置 `known-first-party = ["app"]`。 ### 日志记录 使用 `app.utils.logger.get_logger(__name__)` 获取 logger 实例,不要直接使用 print。 ## 🤝 贡献指南 欢迎提交 Issue 和 Pull Request! 1. Fork 本仓库 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`) 3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) 4. 推送到分支 (`git push origin feature/AmazingFeature`) 5. 开启 Pull Request ## 📄 许可证 MIT License