20 KiB
QtDesktopPet 本地工作区 Agent 独立窗口化施工文档
0. 本次施工目标
当前项目已经有“本地工作区 Agent”面板雏形,能够显示:
- 当前工作区路径
- 索引项数量
- 最近扫描时间
- 上一轮搜索结果
- 选中文件详情
- 文本命中
- 最近读取
- 读取 / 打开 / 定位 等操作按钮
但当前形态更接近“文件搜索面板”,还不是类似 Codex / Claude Code 的工作区 Agent 面板。
本次施工目标:
将本地工作区 Agent 从桌宠窗口中独立出来,做成一个单独的非模态主窗口,并改造成“左侧工作区导航 + 中间 Agent 对话 + 右侧上下文 / 文件详情 / 变更确认 + 底部任务输入框”的 Codex 风格面板。
1. 产品定位
1.1 桌宠窗口职责
桌宠本体只负责:
1. 桌宠动画显示
2. 气泡提示
3. 托盘控制
4. 打开设置窗口
5. 打开本地工作区 Agent 窗口
6. 显示任务状态,例如“正在分析”“等待确认”“修改完成”
桌宠窗口不应该承载:
1. 文件树
2. 搜索结果列表
3. 多轮 Agent 对话
4. 文件内容预览
5. Diff 预览
6. 修改计划确认
7. 复杂任务日志
1.2 本地工作区 Agent 窗口职责
本地工作区 Agent 独立窗口负责:
1. 选择 / 切换工作区
2. 扫描并索引工作区
3. 显示文件树 / 搜索结果 / 上下文文件
4. 支持用户输入任务
5. 显示 Agent 对话过程
6. 显示工具调用过程
7. 显示已读取文件
8. 显示待确认操作
9. 显示 Diff 或修改计划
10. 执行确认后的文件操作
2. 为什么必须做成独立窗口
2.1 当前问题
如果本地工作区 Agent 和桌宠共用一个窗口,会导致:
1. PetWindow 职责继续膨胀
2. 桌宠动画逻辑和工作区 UI 强耦合
3. UI 尺寸冲突,桌宠本体不适合承载复杂面板
4. 复杂任务状态难以展示
5. 后续 Diff、文件树、搜索、日志、对话区很难布局
6. 用户无法像使用 IDE / Codex 一样长时间停留在 Agent 面板中
2.2 独立窗口优势
独立窗口可以获得:
1. 更像 Codex / Claude Code 的工作流
2. 可最大化 / 最小化 / 单独移动
3. 不影响桌宠动画
4. 可以保存窗口大小和位置
5. 可以长期展示复杂任务
6. 可以后续加入 Diff 面板、文件树、任务日志
7. PetWindow 只作为入口,整体架构更干净
3. 总体架构
新的调用关系应为:
PetWindow
↓
openWorkspaceAgentWindow()
↓
WorkspaceAgentWindow
↓
WorkspacePanel
↓
WorkspaceController
↓
WorkspaceAgent
↓
WorkspaceToolRegistry
↓
Workspace Tools
其中:
PetWindow:
只负责打开窗口和接收状态提示。
WorkspaceAgentWindow:
独立顶层窗口,负责窗口行为。
WorkspacePanel:
主 UI 容器,负责布局。
WorkspaceController:
UI 和业务逻辑之间的桥梁。
WorkspaceAgent:
处理用户任务、文件分析、工具调用计划。
WorkspaceToolRegistry:
管理文件工具。
Workspace Tools:
读取、搜索、打开、创建、移动、删除到回收站等具体工具。
4. 新增 UI 目录结构
建议新增:
src/ui/workspace/
├── WorkspaceAgentWindow.h
├── WorkspaceAgentWindow.cpp
├── WorkspacePanel.h
├── WorkspacePanel.cpp
├── WorkspaceTopBar.h
├── WorkspaceTopBar.cpp
├── WorkspaceSidebar.h
├── WorkspaceSidebar.cpp
├── WorkspaceChatView.h
├── WorkspaceChatView.cpp
├── WorkspaceInspector.h
├── WorkspaceInspector.cpp
├── WorkspaceInputBar.h
├── WorkspaceInputBar.cpp
├── WorkspacePlanReviewWidget.h
├── WorkspacePlanReviewWidget.cpp
├── WorkspaceDiffView.h
└── WorkspaceDiffView.cpp
第一阶段可以先实现:
WorkspaceAgentWindow
WorkspacePanel
WorkspaceTopBar
WorkspaceSidebar
WorkspaceChatView
WorkspaceInspector
WorkspaceInputBar
WorkspacePlanReviewWidget 和 WorkspaceDiffView 可以先放空壳,后续接写操作确认。
5. WorkspaceAgentWindow 设计
5.1 类型选择
建议使用:
class WorkspaceAgentWindow : public QWidget
或者:
class WorkspaceAgentWindow : public QMainWindow
推荐第一版使用 QWidget,因为当前项目 UI 结构大多基于 QWidget,迁移成本低。
窗口必须是独立顶层窗口,不要作为 PetWindow 的子控件。
创建时:
m_workspaceAgentWindow = std::make_unique<WorkspaceAgentWindow>();
m_workspaceAgentWindow->show();
m_workspaceAgentWindow->raise();
m_workspaceAgentWindow->activateWindow();
不要传 this 作为 parent。
5.2 窗口行为
建议窗口属性:
setWindowTitle(QStringLiteral("本地工作区 Agent"));
resize(1180, 760);
setMinimumSize(960, 600);
setWindowFlags(Qt::Window);
不要使用:
Qt::Tool
原因:
Qt::Tool 更像附属工具窗,可能不显示在任务栏,不适合 Codex 风格主面板。
5.3 关闭行为
建议关闭窗口时只隐藏,不销毁。
实现思路:
void WorkspaceAgentWindow::closeEvent(QCloseEvent *event)
{
event->ignore();
hide();
}
这样:
1. 工作区上下文不会丢失
2. 搜索结果不会丢失
3. Agent 对话不会丢失
4. 再次打开速度更快
退出程序时由主程序统一析构。
6. PetWindow 接入方案
6.1 新增成员
在 PetWindow.h 中增加前置声明:
class WorkspaceAgentWindow;
新增成员:
std::unique_ptr<WorkspaceAgentWindow> m_workspaceAgentWindow;
6.2 新增方法
在 PetWindow 中新增:
void openWorkspaceAgentWindow();
实现逻辑:
void PetWindow::openWorkspaceAgentWindow()
{
if (!m_workspaceAgentWindow)
{
m_workspaceAgentWindow = std::make_unique<WorkspaceAgentWindow>();
}
m_workspaceAgentWindow->show();
m_workspaceAgentWindow->raise();
m_workspaceAgentWindow->activateWindow();
}
注意:
WorkspaceAgentWindow 不要设置 PetWindow 为 parent。
6.3 菜单入口
在桌宠右键菜单增加:
打开本地工作区 Agent
点击后调用:
openWorkspaceAgentWindow();
后续也可以在聊天中识别:
打开工作区
打开 Agent
打开本地工作区
然后调用该窗口。
7. WorkspacePanel 布局设计
7.1 总体布局
目标布局:
┌─────────────────────────────────────────────────────────────┐
│ TopBar:工作区路径 / 索引状态 / 模式 / 按钮 │
├──────────────┬────────────────────────────┬─────────────────┤
│ Sidebar │ ChatView │ Inspector │
│ 文件树/搜索 │ Agent 对话 / 工具调用过程 │ 详情/命中/变更 │
├──────────────┴────────────────────────────┴─────────────────┤
│ InputBar:任务输入框 / 发送 / 停止 / 附加文件 / 模式切换 │
└─────────────────────────────────────────────────────────────┘
7.2 推荐 Qt 布局
使用:
QVBoxLayout *rootLayout;
QHBoxLayout *bodyLayout;
结构:
rootLayout
├── WorkspaceTopBar
├── bodyLayout
│ ├── WorkspaceSidebar
│ ├── WorkspaceChatView
│ └── WorkspaceInspector
└── WorkspaceInputBar
建议宽度比例:
Sidebar: 280 px
ChatView: stretch 1
Inspector: 320 px
可以使用:
bodyLayout->setStretch(0, 0);
bodyLayout->setStretch(1, 1);
bodyLayout->setStretch(2, 0);
8. 顶部栏 WorkspaceTopBar
8.1 显示内容
顶部栏显示:
本地工作区 Agent
工作区:D:/programming/成品项目/项目/Qt_DesktopPet [切换工作区]
索引项:389 最近扫描:2026-06-23 09:49:02 [重新索引]
模式:只读 / 确认写入 [设置]
8.2 按钮
需要以下按钮:
选择工作区
重新索引
清空上下文
停止任务
权限模式
第一版按钮可以先实现:
选择工作区
重新索引
关闭
9. 左侧栏 WorkspaceSidebar
9.1 Tab 结构
左侧栏建议使用 QTabWidget:
文件
搜索
上下文
9.2 文件 Tab
显示文件树。
第一版可以先不用完整树形结构,继续使用当前已有的索引结果列表。
后续再升级为:
QTreeView + QFileSystemModel
或自定义模型。
按钮:
读取
打开
定位
加入上下文
复制路径
9.3 搜索 Tab
包含:
搜索输入框
搜索类型:文件名 / 文本内容
搜索按钮
结果列表
搜索结果点击后:
1. 更新右侧 Inspector
2. 可双击读取
3. 可加入上下文
9.4 上下文 Tab
显示 Agent 当前已经读取或参考过的文件:
已加入上下文:
- README.md
- CMakeLists.txt
- src/workspace/WorkspaceController.cpp
每项支持:
移出上下文
打开
定位
重新读取
10. 中间区 WorkspaceChatView
10.1 职责
中间区是 Agent 面板核心。
它负责显示:
1. 用户输入
2. Agent 回复
3. 工具调用
4. 工具返回结果
5. 文件读取记录
6. 搜索结果摘要
7. 修改计划
8. 错误提示
10.2 消息类型
新增消息类型:
enum class WorkspaceMessageType
{
User,
Assistant,
ToolCall,
ToolResult,
FileRead,
SearchResult,
Plan,
Diff,
Warning,
Error
};
消息结构:
struct WorkspaceMessage
{
WorkspaceMessageType type = WorkspaceMessageType::Assistant;
QString title;
QString content;
QStringList relatedFiles;
QDateTime createdAt;
};
10.3 显示样式
第一版可以用:
QTextEdit
或者:
QScrollArea + QVBoxLayout + 自定义 message widget
推荐第一版用 QScrollArea + QVBoxLayout。
原因:
1. 后续每种消息可以有不同样式
2. 工具调用可以做折叠
3. Diff 可以单独嵌入控件
4. 比 QTextEdit 更适合 Agent UI
11. 右侧栏 WorkspaceInspector
11.1 Tab 结构
右侧栏建议使用 QTabWidget:
详情
命中
变更
11.2 详情 Tab
显示当前选中文件:
src/workspace/WorkspaceAccessPolicy.h
类型:文本文件
大小:1434 字节
修改时间:2026-06-23 04:59:27
相对路径:src/workspace/WorkspaceAccessPolicy.h
[读取] [打开] [定位] [加入上下文]
11.3 命中 Tab
显示文本搜索命中:
README.dev.md:333
搜索包含 class 的文件:搜索文本内容,显示路径、行号和片段
src/workspace/WorkspaceAccessPolicy.h:5
class WorkspaceAccessPolicy
11.4 变更 Tab
用于后续显示待确认修改:
待确认变更:
1. src/ui/workspace/WorkspacePanel.cpp
- 调整为三栏布局
2. src/ui/workspace/WorkspaceInputBar.cpp
- 新增任务输入栏
[查看 Diff] [确认应用] [取消]
第一版可以先显示:
暂无待确认变更
12. 底部输入栏 WorkspaceInputBar
12.1 基本控件
底部输入栏包含:
任务输入框
发送按钮
停止按钮
附加文件按钮
模式按钮
第一版可以只做:
任务输入框
发送按钮
停止按钮
输入框 placeholder:
输入任务,例如:分析这个项目结构,搜索 workspace 相关代码,读取第二个文件……
12.2 快捷命令
后续支持:
/scan 重新扫描工作区
/search 搜索文件或文本
/read 读取文件
/clear 清空上下文
/mode 切换权限模式
第一版不强制实现。
13. 当前面板迁移方案
当前截图中的内容迁移如下:
13.1 当前“上一轮结果”
迁移到:
WorkspaceSidebar -> 搜索 Tab
13.2 当前“选中项”
迁移到:
WorkspaceInspector -> 详情 Tab
13.3 当前“文本命中”
迁移到:
WorkspaceInspector -> 命中 Tab
13.4 当前“最近读取”
迁移到:
WorkspaceSidebar -> 上下文 Tab
或者:
WorkspaceInspector -> 详情 Tab 下方
推荐放到左侧“上下文 Tab”。
13.5 当前底部按钮
当前按钮:
读取
打开
位置
关闭
迁移为:
详情 Tab:
读取
打开
定位
加入上下文
TopBar:
关闭
InputBar:
发送
停止
14. 样式建议
当前面板深色风格可以保留。
建议统一颜色变量:
const QString BackgroundColor = "#0f172a";
const QString PanelColor = "#17233a";
const QString BorderColor = "#2b4164";
const QString PrimaryColor = "#2d5be3";
const QString TextColor = "#ffffff";
const QString MutedTextColor = "#b9c7dd";
不要在每个控件里到处写重复样式。
建议新建:
src/ui/workspace/WorkspaceStyle.h
或在 WorkspacePanel.cpp 内先集中管理。
15. WorkspaceController 对接
UI 不直接调用底层文件工具。
UI 调用:
WorkspaceController
建议接口:
class WorkspaceController
{
public:
bool hasWorkspace() const;
QString workspaceRoot() const;
bool openWorkspace(const QString &rootPath, QString *errorMessage = nullptr);
bool rescan(QString *errorMessage = nullptr);
QVector<WorkspaceFileInfo> indexedFiles() const;
QVector<WorkspaceFileInfo> lastSearchResults() const;
QVector<WorkspaceTextMatch> lastTextMatches() const;
QVector<WorkspaceFileInfo> recentlyReadFiles() const;
WorkspaceToolResult searchFiles(const QString &keyword);
WorkspaceToolResult searchText(const QString &keyword);
WorkspaceToolResult readFile(const QString &relativeOrAbsolutePath);
WorkspaceToolResult openFile(const QString &relativeOrAbsolutePath);
WorkspaceToolResult revealFile(const QString &relativeOrAbsolutePath);
WorkspaceToolResult handleUserTask(const QString &message);
};
第一版 handleUserTask 可以先做规则分发,不必实现完整模型工具调用。
16. Agent 对话第一版行为
第一版不要急着做完整自动 Agent。
先做规则驱动:
用户输入包含“搜索”:
调 searchFiles 或 searchText
用户输入包含“读取 / 打开第几个 / 第二个”:
使用上一轮搜索结果
用户输入包含“分析项目”:
读取 README.md、CMakeLists.txt、main.cpp、src 目录结构,然后交给 AI
用户输入普通问题:
作为普通 AI 对话,但附加当前工作区摘要
17. 确认机制
涉及写操作时,必须进入确认流程。
窗口中显示:
Agent 准备执行以下操作:
操作:移动文件
源路径:src/old/FileOperationManager.cpp
目标路径:src/workspace/old/FileOperationManager.cpp
风险:高
是否确认?
按钮:
确认执行
取消
第一版可以先只展示计划,不真正实现写操作。
18. 施工顺序
阶段 1:独立窗口
目标:
新增 WorkspaceAgentWindow
新增 WorkspacePanel
PetWindow 右键菜单可以打开独立窗口
窗口可以独立移动、最小化、最大化
关闭窗口时隐藏,不销毁
验收:
桌宠窗口和 Agent 窗口彼此独立
关闭 Agent 窗口不退出程序
再次打开保留状态
阶段 2:迁移现有面板
目标:
把当前已有的工作区路径、索引项、搜索结果、选中项、文本命中、最近读取迁移到 WorkspacePanel
验收:
新窗口中能看到当前截图已有功能
读取 / 打开 / 定位功能正常
阶段 3:三栏布局
目标:
顶部 TopBar
左侧 Sidebar
中间 ChatView
右侧 Inspector
底部 InputBar
验收:
界面从“文件检索器”变成“Agent 工作台”
中间区域为对话主区域
左侧和右侧为辅助区域
阶段 4:任务输入栏
目标:
底部可以输入任务
点击发送后添加用户消息
系统根据规则返回 Agent 消息
验收:
用户输入“搜索 workspace”
左侧显示搜索结果
中间显示工具调用过程
右侧显示选中文件详情
阶段 5:工作区选择
目标:
顶部增加“选择工作区”
使用 QFileDialog 选择目录
选择后扫描索引
更新 TopBar 状态
验收:
可以切换工作区
切换后搜索结果清空
索引数量更新
最近扫描时间更新
阶段 6:Agent 对话只读分析
目标:
支持“分析这个项目”
自动读取 README.md、CMakeLists.txt、main.cpp 和 src 目录结构
调用现有 AI 对话能力返回分析结果
验收:
用户可以在面板中输入“分析这个项目”
系统能显示读取了哪些文件
AI 能给出项目结构分析
阶段 7:确认计划与变更区
目标:
右侧增加“变更”Tab
支持展示待确认操作计划
验收:
写操作不会直接执行
会先显示计划
用户确认后才执行
19. CMake 修改
新增文件后,需要加入 CMakeLists.txt。
第一阶段至少加入:
src/ui/workspace/WorkspaceAgentWindow.h
src/ui/workspace/WorkspaceAgentWindow.cpp
src/ui/workspace/WorkspacePanel.h
src/ui/workspace/WorkspacePanel.cpp
src/ui/workspace/WorkspaceTopBar.h
src/ui/workspace/WorkspaceTopBar.cpp
src/ui/workspace/WorkspaceSidebar.h
src/ui/workspace/WorkspaceSidebar.cpp
src/ui/workspace/WorkspaceChatView.h
src/ui/workspace/WorkspaceChatView.cpp
src/ui/workspace/WorkspaceInspector.h
src/ui/workspace/WorkspaceInspector.cpp
src/ui/workspace/WorkspaceInputBar.h
src/ui/workspace/WorkspaceInputBar.cpp
注意:
当前项目 CMAKE_AUTOMOC 是关闭的。
所以第一版新增 UI 类尽量不要使用 Q_OBJECT。
需要回调时优先使用:
std::function
例如:
void setSendCallback(std::function<void(const QString &)> callback);
20. 编码要求
20.1 大括号风格
使用 Allman 风格:
if (condition)
{
doSomething();
}
20.2 不使用 OutputDebugStringA
不要使用:
OutputDebugStringA
日志使用项目已有 Logger,或者在必要时使用控制台输出。
20.3 不引入复杂依赖
第一版不要引入:
QML
WebView
第三方 Diff 库
SQLite
向量数据库
Everything SDK
先用 Qt Widgets 原生控件完成闭环。
21. 验收清单
完成后必须满足:
1. Agent 面板是独立顶层窗口。
2. 桌宠窗口关闭 / 隐藏不影响 Agent 面板。
3. Agent 面板关闭时默认隐藏,不丢失状态。
4. 可从桌宠右键菜单打开 Agent 面板。
5. Agent 面板有顶部工作区栏。
6. Agent 面板有左侧文件 / 搜索 / 上下文区域。
7. Agent 面板有中间对话区。
8. Agent 面板有右侧详情 / 命中 / 变更区域。
9. Agent 面板有底部任务输入栏。
10. 可以选择工作区。
11. 可以重新索引。
12. 可以搜索文件。
13. 可以读取文件。
14. 可以打开文件。
15. 可以定位文件。
16. 可以显示最近读取。
17. 可以输入任务并在中间对话区显示用户消息和 Agent 回复。
18. 不发生 PetWindow 继续膨胀的问题。
19. 项目可以正常编译。
22. 最终目标
本次改造完成后,界面形态应从:
文件检索面板
升级为:
Codex 风格的本地工作区 Agent 面板
桌宠负责陪伴和入口,Agent 窗口负责复杂任务。
最终产品体验应类似:
用户打开桌宠
↓
点击“本地工作区 Agent”
↓
弹出独立工作区窗口
↓
选择项目目录
↓
输入“分析这个项目”
↓
Agent 搜索、读取、分析文件
↓
在对话区展示过程和结论
↓
需要修改时展示计划和 Diff
↓
用户确认后执行
这是后续接近 Codex / Claude Code 工作流的基础。