Files
Qt_DesktopPet/docs/QtDesktopPet_后续功能规划与结构审查.md
T

1102 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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. 天气查询
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小时后提醒我看消息
```
桌宠创建本地提醒,到点后气泡提示或托盘提示。
当前实现状态:
```text
已新增 src/reminder/ 模块
已支持一次性提醒解析、JSON 持久化、启动后加载、到点触发和状态标记
已支持聊天创建 / 查询 / 取消提醒
已支持设置页按状态查看提醒、取消 pending 提醒、清理已触发/已取消历史
已支持 reminder_default / reminder_soft 内置音效
已支持用户 wav 音效导入、删除、切换和试听
提醒触发时使用当前设置页选择的全局音效,ReminderItem.soundId 仅保留为历史兼容字段
已接入 Qt Multimedia / QSoundEffect 播放提醒音效
已预留 NotificationDispatcher,当前 Windows 仍由托盘通知承接
```
## 5.2 第一版范围
要做:
```text
一次性提醒
本地保存
程序重启后提醒不丢
到点后触发气泡和托盘通知
提醒触发后标记 triggered
支持查询当前提醒列表
支持取消提醒
```
暂不做:
```text
重复提醒
农历提醒
复杂日历
跨设备同步
联网日程
语音提醒
```
## 5.3 建议目录
```text
src/reminder/
├── ReminderTypes.h
├── ReminderCommandHandler.h
├── ReminderCommandHandler.cpp
├── ReminderParser.h
├── ReminderParser.cpp
├── ReminderManager.h
├── ReminderManager.cpp
├── ReminderStore.h
├── ReminderStore.cpp
├── ReminderSoundRepository.h
├── ReminderSoundRepository.cpp
├── ReminderSoundPlayer.h
└── ReminderSoundPlayer.cpp
```
## 5.4 数据结构建议
```cpp
struct ReminderItem
{
QString id;
QString title;
QString originalText;
QDateTime remindAt;
ReminderStatus status = ReminderStatus::Pending;
QDateTime createdAt;
QString soundId; // 历史兼容字段,触发时不再读取
};
```
## 5.5 存储文件
保存到:
```text
QStandardPaths::AppConfigLocation/reminders.json
```
示例:
```json
{
"reminders": [
{
"id": "reminder_001",
"title": "提交作业",
"originalText": "晚上8点提醒我提交作业",
"remindAt": "2026-06-01T20:00:00",
"status": "pending",
"createdAt": "2026-06-01T15:20:00",
"soundId": ""
}
]
}
```
配置损坏时备份:
```text
reminders.broken.yyyyMMdd-HHmmss.json
```
## 5.6 时间解析策略
第一版规则解析优先,不依赖 AI。
支持:
```text
8点
8点30
20:30
晚上8点
下午3点
明天9点
明天上午10点
后天9点
今天下午3点
6月3日9点
6/3 09:00
下周一上午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 + 切 happy,无 happy 时回退 talk,不发 Windows 通知
桌宠隐藏:播放当前全局音效,触发 Windows 托盘通知,不在下次显示时补气泡
用户拖动中:播放当前全局音效,不打断 drag,拖动结束后显示气泡,不发 Windows 通知
```
提醒文案:
```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
当前已落地一次性提醒、提醒列表、取消提醒、到点气泡/托盘通知和提醒音效管理。
后续可继续补确认/稍后提醒、重复提醒和跨平台通知实现。
```
## 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
定时提醒:已作为第一个工具能力落地
天气:建议第二个落地
本地文件操作:风险较高,第三个落地
联网搜索:通用能力,最后落地
```