# QtDesktopPet 后续功能开发与当前结构审查文档 > 适用仓库:`https://git.emoera.com/Make/Qt_DesktopPet` > 目的:在继续新增功能前,先收口当前结构风险,并为后续功能扩展确定统一入口和实现边界。 > 注意:本文档不是让 Codex 一次性实现所有功能,而是作为后续开发路线和代码审查依据。 --- ## 1. 当前项目状态概览 根据当前 README 和仓库结构,项目已经具备较完整的桌宠应用基础: - 透明无边框桌宠窗口 - 鼠标拖动、置顶、右键菜单 - 托盘显示、隐藏、退出 - 单实例限制 - PNG 序列帧动画播放 - `idle / talk / think / sleep / happy / drag / error` 多状态动画 - 隐藏时暂停动画,显示时恢复 - 窗口位置、置顶、缩放、性能设置保存 - 文件日志和日志轮转 - AI Provider 分组配置 - OpenAI Compatible 聊天请求 - Google Gemini 原生聊天请求 - SSE 流式输出 - AI 回复气泡 - 聊天输入框 - 对话历史面板 - 本地历史保存和历史上限 - AI 请求取消和对话清空 - 角色文件夹导入和角色切换 - 删除用户导入角色 - Windows 打包脚本和 Inno Setup 安装器脚本 - Release exe 双击不弹控制台窗口 项目已经从早期 MVP 进入到“可扩展桌面应用原型”阶段,可以开始规划工具能力扩展。 但是,在正式加入定时提醒、天气、本地文件操作、联网搜索之前,建议先做一轮结构收口。 --- ## 2. 当前需要优先审查或优化的地方 ### 2.1 审查角色切换是否保存后立即生效 当前已经支持角色导入和角色切换。经当前测试确认: ```text 设置页选择新角色 保存设置 当前桌宠窗口是否立即重新加载新角色包 ``` 结论:保存后会立即切换,不依赖重启。 当前代码路径: ```text PetWindow::openSettingsDialog() ↓ applyAppConfig(dialog.appConfig()) ↓ PetWindow::applyAppConfig() 比较旧 characterId 与新 characterId ↓ characterId 变化时停止当前动画并调用 loadCharacterPackage() ↓ loadCharacterPackage() 重新构建动画状态并从 idle 状态开始播放 ``` 后续不需要围绕 2.1 做修复,只需要保留为回归检查项: ```text 设置页切换角色后点击保存,桌宠应立即显示新角色 不需要关闭或重启程序 切换后动画状态应可继续正常播放 ``` --- ### 2.2 不要继续向 PetWindow 堆功能 目前 `PetWindow` 已经承担了过多职责,后续不能继续把所有新功能塞进去。 `PetWindow` 应该主要负责: ```text 窗口显示 拖动 菜单 子组件组织 基础 UI 信号转发 ``` 不建议继续负责: ```text 提醒解析 天气查询 联网搜索 本地文件读写 AI 工具调度 复杂业务状态管理 ``` 后续应逐步拆出: ```text ChatController ReminderController WeatherController FileOperationController ToolCommandDispatcher ``` 即使暂时不大规模重构,也至少要为后续功能增加统一调度层,不要直接把新逻辑写进 `PetWindow::submitChatMessage()`。 --- ### 2.3 新增 IntentRouter / CommandDispatcher 后续功能包括: ```text 定时提醒 天气查询 本地文件操作 联网搜索 普通 AI 对话 ``` 这些都来自用户输入框。如果没有统一入口,逻辑会混乱。 可拆成两个模块: ```text src/intent/ ├── IntentTypes.h ├── IntentRouter.h └── IntentRouter.cpp src/command/ ├── CommandDispatcher.h └── CommandDispatcher.cpp ``` 当前阶段已选择合并为一个轻量模块: ```text src/assistant/ ├── UserIntent.h ├── IntentRouter.h ├── IntentRouter.cpp ├── CommandDispatcher.h └── CommandDispatcher.cpp ``` 当前意图枚举: ```cpp enum class UserIntentType { Chat, Reminder, Weather, FileOperation, Search }; ``` 建议入口流程: ```text 用户输入 ↓ IntentRouter 判断意图 ↓ CommandDispatcher 分发 ↓ ReminderManager / WeatherManager / FileOperationManager / WebSearchManager / ConversationManager ``` 第一版意图识别不需要复杂,规则优先即可。 --- ### 2.4 意图优先级建议 多个意图可能同时出现,需要固定优先级。 推荐: ```text Reminder > FileOperation > Weather > Search > Chat ``` 原因: ```text “明天早上提醒我看天气” 虽然包含“天气”,但本质是提醒。 “把天气截图保存到桌面” 可能是文件操作,而不是单纯天气查询。 “搜索一下明天天气” 可能是搜索请求,但如果已有 WeatherTool,应优先走天气工具。 ``` --- ### 2.5 检查 CMAKE_AUTOMOC 设置 如果后续新增的 Manager 使用 `QObject`、`signals`、`slots`,建议开启: ```cmake set(CMAKE_AUTOMOC ON) ``` 如果继续保持 `CMAKE_AUTOMOC OFF`,则后续模块应避免 `Q_OBJECT`,全部使用普通 C++ 回调或现有信号体系。 建议明确选择一种: ```text 方案 A:开启 AUTOMOC,后续工具模块正常使用 QObject 信号槽 方案 B:保持 OFF,后续模块不得引入 Q_OBJECT ``` 当前阶段选择方案 B:继续保持 `CMAKE_AUTOMOC OFF`,新增意图分发模块使用普通 C++ 类和同步返回值,不引入 `Q_OBJECT`。 后续如果 Reminder / Weather / Search 等模块需要大量跨对象异步信号,再单独评估是否切换到方案 A。 --- ### 2.6 README 与实际功能保持同步 当前 README 已经比较完整,但后续每实现一个功能,应同步更新: ```text 当前状态 配置说明 隐私说明 日志说明 用户数据目录 发布包包含内容 ``` 尤其是以下功能需要额外说明: ```text 提醒数据保存位置 天气 API 请求说明 IP 定位隐私说明 本地文件操作权限说明 联网搜索来源和隐私说明 ``` --- ### 2.7 继续保持角色导入安全策略 当前角色导入已经有较多安全边界,比如: ```text 只导入本地文件夹 验证失败不复制 角色 id 安全字符限制 内置角色不能被覆盖 只允许删除用户导入角色 状态路径安全检查 ``` 后续本地文件操作模块也应该沿用这种风格: ```text 路径必须安全 不允许任意系统目录 危险操作必须确认 修改前备份 删除必须二次确认 ``` --- ## 3. 后续功能总路线 建议不要一次做完所有功能,按以下顺序推进: ```text 阶段 0:结构收口 阶段 1:定时提醒 阶段 2:天气查询 阶段 3:本地文件操作 阶段 4:联网搜索 阶段 5:语音对话 / 更复杂 Agent 能力 ``` 当前最推荐先做: ```text 1. IntentRouter / CommandDispatcher 2. 定时提醒 3. 天气查询 ``` --- # 4. 阶段 0:结构收口任务 ## 4.1 目标 在正式加新功能之前,先让用户输入有统一分发入口,避免所有功能混进聊天逻辑。 ## 4.2 当前新增文件 ```text src/assistant/UserIntent.h src/assistant/IntentRouter.h src/assistant/IntentRouter.cpp src/assistant/CommandDispatcher.h src/assistant/CommandDispatcher.cpp ``` ## 4.3 IntentRouter 职责 ```text 判断用户输入属于什么类型: - 普通聊天 - 定时提醒 - 天气查询 - 本地文件操作 - 联网搜索 ``` 第一版可以使用规则判断,不依赖 AI。 ## 4.4 CommandDispatcher 职责 ```text 接收用户输入 调用 IntentRouter 根据意图分发到不同 Manager 普通聊天才交给现有 ConversationManager / LLMProvider ``` ## 4.5 PetWindow 改造要求 `PetWindow` 只负责把用户输入交给 `CommandDispatcher`,不直接判断业务逻辑。 示例流程: ```text PetWindow::submitChatMessage() ↓ CommandDispatcher::dispatch(userText) ↓ 根据返回信号更新气泡、状态机、历史 ``` 不要继续在 `PetWindow` 中直接写提醒、天气、搜索或文件逻辑。 --- # 5. 阶段 1:定时提醒功能 ## 5.1 功能定位 实现本地一次性提醒功能。 用户可以输入: ```text 晚上8点提醒我提交作业 明天9点提醒我开会 10分钟后提醒我喝水 半小时后提醒我休息 2小时后提醒我看消息 ``` 桌宠创建本地提醒,到点后气泡提示或托盘提示。 ## 5.2 第一版范围 要做: ```text 一次性提醒 本地保存 程序重启后提醒不丢 到点后触发气泡和托盘通知 提醒触发后标记 triggered 支持查询当前提醒列表 支持取消提醒 ``` 暂不做: ```text 重复提醒 农历提醒 复杂日历 跨设备同步 联网日程 语音提醒 ``` ## 5.3 建议目录 ```text src/reminder/ ├── ReminderTypes.h ├── ReminderParser.h ├── ReminderParser.cpp ├── ReminderManager.h ├── ReminderManager.cpp ├── ReminderStore.h └── ReminderStore.cpp ``` ## 5.4 数据结构建议 ```cpp struct ReminderItem { QString id; QString title; QString originalText; QDateTime remindAt; bool triggered = false; QDateTime createdAt; }; ``` ## 5.5 存储文件 保存到: ```text QStandardPaths::AppConfigLocation/reminders.json ``` 示例: ```json { "reminders": [ { "id": "reminder_001", "title": "提交作业", "originalText": "晚上8点提醒我提交作业", "remindAt": "2026-06-01T20:00:00", "triggered": false, "createdAt": "2026-06-01T15:20:00" } ] } ``` 配置损坏时备份: ```text reminders.broken.yyyyMMdd-HHmmss.json ``` ## 5.6 时间解析策略 第一版规则解析优先,不依赖 AI。 支持: ```text 8点 8点30 20:30 晚上8点 下午3点 明天9点 明天上午10点 10分钟后 半小时后 1小时后 2小时后 ``` 如果规则解析失败,后续可以再接 AI 解析。 ## 5.7 AI 辅助解析的设计边界 可以后续做 AI 辅助解析,但 AI 不允许直接写文件。 正确流程: ```text AI 解析用户意图 ↓ 返回结构化 JSON ↓ 程序校验 ↓ ReminderManager 创建提醒 ↓ ReminderStore 保存 ``` 禁止: ```text AI 直接读写 reminders.json AI 直接修改本地文件 ``` AI 解析时必须由程序提供当前本地时间: ```text currentLocalTime timeZone weekday userText ``` 因为 API 模型无法可靠知道用户本机时间。 ## 5.8 到点触发 建议行为: ```text 桌宠可见:显示 ChatBubble + 切 talk 或 happy 桌宠隐藏:系统托盘通知 用户拖动中:不打断 drag,拖动结束后显示 ``` 提醒文案: ```text 到时间啦:提交作业 ``` ## 5.9 给 Codex 的阶段任务 ```text 1. 新增 reminder 模块 2. 实现一次性 ReminderItem 3. 实现 ReminderStore JSON 读写 4. 实现 ReminderParser 规则解析 5. 实现 ReminderManager 定时检查 6. 到点后发信号给 UI 层 7. UI 层显示气泡和托盘通知 8. 不要把提醒逻辑写进 PetWindow 9. 不要让 AI 直接写本地文件 ``` --- # 6. 阶段 2:天气查询功能 ## 6.1 功能定位 天气是独立工具能力,不放进联网搜索。 正确流程: ```text 用户问天气 ↓ 程序识别 Weather 意图 ↓ WeatherManager 调用天气 API ↓ 拿到结构化天气数据 ↓ AI 根据结构化数据自然语言回复 ``` 不要让 AI 自己搜索天气,也不要让 AI 自己拼 URL。 ## 6.2 为什么不用联网搜索做天气 搜索天气有几个问题: ```text 结果格式不稳定 摘要可能过期 地区解析不稳定 无法稳定提取温度、降雨、风力、湿度等字段 AI 总结容易出错 ``` 天气应该走天气 API。 ## 6.3 第一版范围 要做: ```text 识别天气请求 支持默认城市 支持用户指定城市 支持当前天气 支持简单今天/明天查询 AI 不可用时模板兜底回复 查询中切 think 回复时切 talk 失败时切 error ``` 暂不做: ```text 天气预警 空气质量详情 分钟级降水 生活指数 地图定位 GPS 定位 自动后台推送 天气提醒 多天气源自动切换 ``` ## 6.4 建议目录 ```text src/weather/ ├── WeatherTypes.h ├── WeatherConfig.h ├── WeatherStore.h ├── WeatherStore.cpp ├── WeatherProvider.h ├── WeatherProvider.cpp ├── OpenMeteoWeatherProvider.h ├── OpenMeteoWeatherProvider.cpp ├── WeatherManager.h └── WeatherManager.cpp ``` ## 6.5 天气源建议 第一版推荐: ```text Open-MeteoProvider ``` 优点: ```text 不需要 API Key 全球可用 适合开源项目默认方案 ``` 后续再加: ```text AmapWeatherProvider OpenWeatherProvider ``` 如果主要面向国内用户,高德天气也可以作为第二个 Provider,但需要用户配置 Web 服务 Key。 ## 6.6 WeatherConfig ```cpp struct WeatherConfig { bool enabled = true; QString provider = "open-meteo"; QString defaultLocationName; double defaultLatitude = 0.0; double defaultLongitude = 0.0; bool hasDefaultCoordinate = false; bool allowIpLocation = false; bool askBeforeIpLocation = true; QString apiKey; int timeoutMs = 10000; bool useAIResponse = true; bool showRawDataWhenAIUnavailable = true; }; ``` 保存到: ```text QStandardPaths::AppConfigLocation/weather_config.json ``` ## 6.7 地点策略 优先级: ```text 用户明确输入城市 ↓ 用户设置的默认城市 ↓ 用户主动选择 IP 定位 ↓ 追问用户城市 ``` 不要默认静默使用 IP 定位。 ## 6.8 IP 定位原则 IP 定位可以做,但只能作为可选辅助。 原因: ```text VPN / 代理会导致定位错误 运营商出口可能定位不准 校园网 / 公司网可能不准 用户可能不希望程序自动请求定位服务 ``` 建议交互: ```text 你还没有设置默认城市。 可以手动输入城市,也可以使用 IP 自动定位。 是否使用 IP 自动定位? ``` 识别到城市后仍要确认: ```text 我识别到你可能在西安,要设为默认城市吗? ``` ## 6.9 天气意图关键词 ```text 天气 气温 温度 冷不冷 热不热 下雨 降雨 带伞 刮风 风力 湿度 空气质量 雾霾 适合出门 穿什么 外面怎么样 ``` ## 6.10 AI 回复上下文 程序应把结构化天气数据交给 AI: ```text 用户问题: 今天西安天气怎么样? 天气数据: 城市:西安 当前天气:多云 温度:26℃ 体感温度:27℃ 湿度:60% 风向:东北风 风速:3级 降雨概率:20% 更新时间:2026-06-01 15:00 请根据以上数据,用简洁自然的语气回答。不要编造没有提供的数据。 ``` ## 6.11 AI 不可用时兜底 如果天气 API 成功但 AI 失败,则模板回复: ```text 西安当前天气:多云,26℃,湿度 60%,风力 3 级。更新时间:15:00。 ``` --- # 7. 阶段 3:本地文件操作功能 ## 7.1 功能定位 本地文件操作是高风险功能,必须后置,不能早于提醒和天气。 目标是让桌宠能辅助用户处理本地文件,例如: ```text 帮我整理这个文件夹里的图片 帮我把这些日志文件打包 帮我读取这个 txt 总结一下 帮我把这个 md 转成备份副本 ``` ## 7.2 基本原则 AI 不能直接操作本地文件。 正确流程: ```text AI 理解用户意图 ↓ 生成操作计划 ↓ 程序校验路径和权限 ↓ 展示计划给用户确认 ↓ 程序执行 ↓ 执行结果反馈给 AI / 用户 ``` 禁止: ```text AI 直接读写任意文件 AI 直接删除文件 AI 直接覆盖文件 AI 自己决定访问系统目录 ``` ## 7.3 安全边界 必须实现: ```text 工作目录白名单 路径标准化 禁止符号链接逃逸 禁止访问系统目录 禁止访问用户隐私目录,除非用户显式选择 修改前备份 删除前二次确认 批量操作前显示操作计划 ``` ## 7.4 建议目录 ```text src/fileops/ ├── FileOperationTypes.h ├── FileOperationPlanner.h ├── FileOperationPlanner.cpp ├── FileOperationManager.h ├── FileOperationManager.cpp ├── FileSandbox.h ├── FileSandbox.cpp ├── FileBackupManager.h └── FileBackupManager.cpp ``` ## 7.5 第一版建议只做低风险操作 第一版可做: ```text 读取用户主动选择的文本文件 列出用户主动选择的文件夹 复制文件 创建备份 打包 zip 重命名文件,需确认 ``` 暂不做: ```text 删除文件 覆盖文件 移动大量文件 修改源码 执行脚本 运行命令 访问系统目录 ``` ## 7.6 修改/删除必须二次确认 即使用户说“删掉它”,也必须确认: ```text 即将删除: D:/xxx/a.txt D:/xxx/b.txt 此操作会移动到回收站/备份后删除。 是否继续? ``` --- # 8. 阶段 4:联网搜索功能 ## 8.1 功能定位 联网搜索后置,不要现在优先做。 联网搜索是通用增强能力: ```text 查询最新信息 查官网 查报错 查新闻 查版本 查文档 ``` ## 8.2 不要给每家模型各自配置搜索 不建议: ```text OpenAIProvider 自己一套搜索 GeminiProvider 自己一套搜索 DeepSeekProvider 自己一套搜索 CustomProvider 自己一套搜索 ``` 推荐: ```text WebSearchManager 独立负责搜索 LLMProvider 只负责回答 ConversationManager / CommandDispatcher 负责把搜索结果注入给 AI ``` ## 8.3 建议目录 ```text src/search/ ├── SearchTypes.h ├── SearchProvider.h ├── SearXNGSearchProvider.h ├── SearXNGSearchProvider.cpp ├── WebSearchManager.h └── WebSearchManager.cpp ``` 第一版只做: ```text SearXNG + 自定义 Base URL ``` 后续再加: ```text Tavily Brave Search CustomSearchProvider ``` ## 8.4 安全边界 ```text 不要让 AI 自己生成任意 URL 给程序访问 不要做网页全文抓取第一版 不要默认每次都联网 不要把用户隐私查询写日志 搜索结果要显示来源 ``` --- # 9. 建议最终开发顺序 ## 9.1 第一步:收口架构 ```text 1. 角色切换保存后立即生效已确认,后续作为回归项保留 2. 新增 IntentRouter 3. 新增 CommandDispatcher 4. 调整 PetWindow 输入流程 5. 当前阶段保持 CMAKE_AUTOMOC OFF,新增模块不使用 Q_OBJECT ``` ## 9.2 第二步:定时提醒 ```text 1. ReminderTypes 2. ReminderStore 3. ReminderParser 4. ReminderManager 5. 到点气泡和托盘通知 6. 设置页增加提醒列表,可后置 ``` ## 9.3 第三步:天气查询 ```text 1. WeatherTypes 2. WeatherConfig / WeatherStore 3. OpenMeteoWeatherProvider 4. WeatherManager 5. IntentRouter 天气识别 6. AI 回复 / 模板兜底 ``` ## 9.4 第四步:本地文件操作 ```text 1. FileSandbox 2. FileOperationTypes 3. FileOperationPlanner 4. FileOperationManager 5. 用户确认 UI 6. 备份机制 ``` ## 9.5 第五步:联网搜索 ```text 1. SearchTypes 2. SearchProvider 3. SearXNGSearchProvider 4. WebSearchManager 5. 来源展示 ``` --- # 10. 给 Codex 的总提示 下面这段可以直接作为后续任务总说明: ```text 当前不要急着直接实现提醒、天气、文件操作或联网搜索。 请先审查当前 QtDesktopPet 项目结构,并完成以下准备工作: 1. 角色切换在设置保存后已确认立即生效,后续只需保留回归检查。 2. 新增 IntentRouter / CommandDispatcher,使用户输入先经过统一意图分发。 3. 意图类型包括 Chat、Reminder、Weather、FileOperation、Search。 4. 意图优先级为 Reminder > FileOperation > Weather > Search > Chat。 5. 普通聊天继续走现有 AI 对话流程。 6. 新功能不得继续直接塞进 PetWindow。 7. 后续 Reminder、Weather、FileOperation、Search 均应作为独立模块接入。 8. 当前阶段保持 CMAKE_AUTOMOC OFF,后续模块不要使用 Q_OBJECT;确需 Qt 信号槽时再单独评估。 9. 保持现有隐私策略:API Key、Authorization、完整用户消息、完整错误响应不得写入日志。 10. 保持现有文件安全策略:路径校验、危险操作确认、修改前备份。 ``` --- # 11. 当前结论 项目已经可以进入“工具能力扩展阶段”,但不建议直接开写业务功能。 建议先做: ```text 结构收口 → IntentRouter / CommandDispatcher → 定时提醒 → 天气 → 本地文件操作 → 联网搜索 ``` 其中: ```text 定时提醒:最适合第一个落地 天气:第二个落地 本地文件操作:风险较高,第三个落地 联网搜索:通用能力,最后落地 ```