21 KiB
DesktopPet 本地工作区 Agent 施工计划
1. 定位
本地工作区 Agent 是下一阶段核心功能。它不是继续维护旧版“文件操作 v1”,而是新增一个独立的工作区系统:
用户选择一个本地工作区目录后,桌宠可以读取、搜索、分析该目录内的文件,并在用户确认后创建、修改、复制、移动、重命名或删除到回收站。
第一阶段优先实现文件级工具,不做 Shell 命令执行,不做脚本执行,不做自动安装依赖。
2. 总原则
- 不允许 AI 直接获得全盘文件权限。
- 不允许 AI 自由执行系统命令。
- 不允许永久删除文件。
- 不允许绕过用户确认修改本地文件。
- 不允许默认读取敏感文件。
- 所有写操作必须先生成计划,展示路径、动作和风险,用户确认后才能执行。
- 所有文件路径必须限制在用户选择的工作区内。
PetWindow只负责 UI 入口、展示和确认,不承载复杂业务逻辑。- 新模块先新增、后替换旧
src/fileops/。
3. 分期目标
当前执行状态:
- v1a.1/v1b/v1b.5 已落地到
src/workspace/和src/ui/WorkspaceAgentWindow.*。 - 当前不持久化工作区路径,不执行写操作,不替换旧
src/fileops/。 - v1b 已支持打开工作区内普通文件、打开文件夹、显示文件所在位置。
- v1b.5 已提供独立 Agent 工作区窗口、右键/托盘入口和默认
Alt+A呼出快捷键;窗口包含左侧结果导航、中间任务对话、右侧详情/上下文和 v1c 预留变更区。
v1a:只读工作区
目标:
- 选择工作区。
- 显示当前工作区。
- 扫描工作区文件树。
- 搜索文件名。
- 搜索文本内容。
- 读取文本文件。
- 分析项目结构。
- 总结文件内容。
验收:
- 工作区外路径被拒绝。
- 系统目录被拒绝。
.git/build/node_modules/dist/release_packages等目录被跳过。- 敏感文件默认拒绝读取。
- 二进制文件拒绝读取。
v1b:打开和定位
状态:已落地。
目标:
- 打开工作区内普通文件。
- 打开工作区内文件夹。
- 显示文件所在位置。
验收:
- 危险后缀拒绝打开。
.lnk不直接打开。- 工作区外路径拒绝。
- 打开文件必须二次确认,打开文件夹作为低风险操作直接执行。
v1c:低风险写入
进入 v1c 前已完成:
创建 test.txt / 新建文件 / 写入 README / 修改 main.cpp明确拒绝,不再误进入旧 fileops 读取文件流程。- 独立 Agent 窗口可以分区展示上一轮结果、文本命中、上下文、选中项详情和最近读取文件。
- 窗口支持对选中结果执行读取、打开和显示位置。
- 窗口可通过桌宠右键菜单、托盘菜单和可配置全局快捷键呼出。
目标:
- 创建文本文件。
- 创建文件夹。
- 复制单个文件。
- 同目录重命名文件。
验收:
- 所有写操作必须确认。
- 目标文件或目录已存在时拒绝覆盖。
- 文件名非法时拒绝。
- 危险后缀拒绝创建或写入。
v1d:高风险写入
目标:
- 修改已有文本文件。
- 移动单个文件。
- 删除文件到回收站。
- 删除文件夹到回收站。
验收:
- 修改前必须备份。
- 移动和删除必须强确认。
- 删除只进入回收站。
- 批量删除第一阶段拒绝。
- 失败时不能破坏原文件。
v1e:替换旧文件操作
目标:
FileOperation意图迁移为Workspace。PetWindow::handleFileOperationChatMessage()过渡为调用WorkspaceController。- 新模块覆盖旧能力后删除旧
src/fileops/。
验收:
- 无
FileOperationManager有效引用。 - CMake 不再登记旧 fileops 文件。
- README 和测试文档改为“本地工作区 Agent”。
4. 新目录结构
建议新增:
src/workspace/
├── WorkspaceTypes.h
├── WorkspaceSession.h
├── WorkspaceSession.cpp
├── WorkspaceIndex.h
├── WorkspaceIndex.cpp
├── WorkspaceScanner.h
├── WorkspaceScanner.cpp
├── WorkspaceAccessPolicy.h
├── WorkspaceAccessPolicy.cpp
├── WorkspaceTool.h
├── WorkspaceToolRegistry.h
├── WorkspaceToolRegistry.cpp
├── WorkspaceAgent.h
├── WorkspaceAgent.cpp
├── WorkspaceController.h
├── WorkspaceController.cpp
├── WorkspaceHistory.h
├── WorkspaceHistory.cpp
└── tools/
├── ListFilesTool.h
├── ListFilesTool.cpp
├── ReadFileTool.h
├── ReadFileTool.cpp
├── SearchFilesTool.h
├── SearchFilesTool.cpp
├── SearchTextTool.h
├── SearchTextTool.cpp
├── OpenFileTool.h
├── OpenFileTool.cpp
├── CreateFileTool.h
├── CreateFileTool.cpp
├── CreateDirectoryTool.h
├── CreateDirectoryTool.cpp
├── WriteFileTool.h
├── WriteFileTool.cpp
├── CopyFileTool.h
├── CopyFileTool.cpp
├── MoveFileTool.h
├── MoveFileTool.cpp
├── RenameFileTool.h
├── RenameFileTool.cpp
├── DeleteToTrashTool.h
└── DeleteToTrashTool.cpp
第一阶段继续保持 CMAKE_AUTOMOC OFF,新增业务类不使用 Q_OBJECT。
5. 核心类型
enum class WorkspacePermissionMode
{
ReadOnly,
ConfirmBeforeWrite,
Advanced
};
第一阶段默认 ConfirmBeforeWrite,但所有写操作仍必须确认。Advanced 只预留,不启用自动写入。
enum class WorkspaceToolRisk
{
ReadOnly,
Low,
Medium,
High,
Dangerous
};
风险定义:
ReadOnly:列目录、搜索、读取普通文本文件。Low:打开文件夹、显示文件所在位置。Medium:打开普通文件、创建文件、创建文件夹、复制文件。High:修改文件、移动文件、重命名文件、删除到回收站。Dangerous:永久删除、执行命令、运行脚本、修改系统目录。
第一阶段遇到 Dangerous 直接拒绝。
struct WorkspaceFileInfo
{
QString relativePath;
QString absolutePath;
QString fileName;
QString suffix;
qint64 sizeBytes = 0;
QDateTime lastModified;
bool isDirectory = false;
bool isTextFile = false;
bool isHidden = false;
};
enum class WorkspaceActionType
{
Unknown,
ListFiles,
SearchFiles,
SearchText,
ReadFile,
OpenFile,
OpenDirectory,
RevealInExplorer,
CreateFile,
CreateDirectory,
WriteFile,
CopyFile,
MoveFile,
RenameFile,
DeleteToTrash
};
struct WorkspaceActionPlan
{
QString id;
WorkspaceActionType type = WorkspaceActionType::Unknown;
WorkspaceToolRisk risk = WorkspaceToolRisk::ReadOnly;
QString title;
QString description;
QString sourcePath;
QString targetPath;
QString relativeSourcePath;
QString relativeTargetPath;
QString content;
QString searchKeyword;
QStringList warnings;
bool requiresConfirmation = false;
bool allowOverwrite = false;
bool confirmed = false;
};
struct WorkspaceToolResult
{
bool success = false;
QString message;
QString errorMessage;
QString outputText;
QString targetPath;
QVector<WorkspaceFileInfo> files;
};
6. 安全策略
WorkspaceAccessPolicy 是最重要的模块,所有工具必须经过它校验。
职责:
- 路径标准化。
- 判断路径是否在工作区内。
- 阻止
../跳出工作区。 - 阻止符号链接跳出工作区。
- 阻止系统目录。
- 阻止危险文件类型。
- 阻止敏感文件默认读取。
- 限制写入行为。
- 限制删除行为。
建议接口:
class WorkspaceAccessPolicy
{
public:
explicit WorkspaceAccessPolicy(const QString &workspaceRoot);
QString workspaceRoot() const;
QString normalizePath(const QString &path) const;
QString toAbsolutePath(const QString &relativeOrAbsolutePath) const;
QString toRelativePath(const QString &absolutePath) const;
bool isInsideWorkspace(const QString &path, QString *errorMessage = nullptr) const;
bool hasSymlinkSegment(const QString &path, QString *errorMessage = nullptr) const;
bool isSystemPath(const QString &path) const;
bool isDangerousSuffix(const QString &path) const;
bool isSensitiveFile(const QString &path) const;
bool isSupportedTextFile(const QString &path) const;
bool canReadFile(const QString &path, QString *errorMessage = nullptr) const;
bool canReadDirectory(const QString &path, QString *errorMessage = nullptr) const;
bool canCreateFile(const QString &path, QString *errorMessage = nullptr) const;
bool canCreateDirectory(const QString &path, QString *errorMessage = nullptr) const;
bool canWriteFile(const QString &path, QString *errorMessage = nullptr) const;
bool canMoveFile(const QString &sourcePath, const QString &targetPath, QString *errorMessage = nullptr) const;
bool canDeleteToTrash(const QString &path, QString *errorMessage = nullptr) const;
};
禁止危险后缀:
.exe .bat .cmd .ps1 .vbs .msi .scr .reg .com .pif .lnk
敏感文件默认拒绝:
.env
*.pem
*.key
id_rsa
id_ed25519
*.pfx
*.p12
credentials.json
secrets.json
token.json
文件名包含以下关键词也视为敏感:
secret
token
password
credential
private
apikey
api_key
7. 扫描和索引
WorkspaceScanner 第一版使用 QDir + 手动目录栈扫描工作区,后续如需取消、进度回调或更细粒度过滤,再考虑替换为 QDirIterator 或异步扫描。
默认限制:
- 最大递归深度:8。
- 最大文件数量:5000。
- 跳过隐藏目录。
- 跳过常见产物目录。
跳过目录:
.git
.vs
.idea
build
cmake-build-debug
cmake-build-release
node_modules
dist
release
release_packages
logs
WorkspaceIndex 第一版只做内存索引:
class WorkspaceIndex
{
public:
void clear();
void setFiles(const QVector<WorkspaceFileInfo> &files);
QVector<WorkspaceFileInfo> allFiles() const;
QVector<WorkspaceFileInfo> findByName(const QString &keyword, int maxResults) const;
QVector<WorkspaceFileInfo> findBySuffix(const QStringList &suffixes, int maxResults) const;
QVector<WorkspaceFileInfo> recentFiles(int maxResults) const;
};
8. 工具清单
8.1 只读工具
ListFilesTool
- 列出工作区内目录。
- 最多显示 200 项。
- 不递归或浅递归。
ReadFileTool
- 只读文本文件。
- 最大读取 128KB。
- 超出截断。
- 敏感文件默认拒绝。
- 不读二进制文件。
支持后缀:
.cpp .h .hpp .c .cc .py .js .ts .json .md .txt .ini .xml .yaml .yml .cmake .pro .css .html
SearchFilesTool
- 只搜索当前工作区索引。
- 最多返回 30 个结果。
- 按相关性和最近修改时间排序。
SearchTextTool
- 最多扫描 1000 个文本文件。
- 单文件最大读取 128KB。
- 最多返回 50 条匹配。
- 每条显示文件路径、行号、片段。
8.2 打开工具
OpenFileTool
- 用系统默认程序打开文件。
- 只能打开工作区内普通文件。
- 危险后缀拒绝。
- 敏感文件需要确认或第一版拒绝。
OpenDirectoryTool
- 打开工作区内目录。
- 工作区外路径拒绝。
RevealInExplorerTool
- 第一版可以先打开所在文件夹。
- 如果后续使用
explorer.exe /select,必须固定程序路径和参数,不接受聊天文本命令。
8.3 写入工具
CreateFileTool
- 只能在工作区内创建。
- 目标不能已存在。
- 文件名必须合法。
- 危险后缀拒绝。
- 必须确认。
CreateDirectoryTool
- 只能在工作区内创建。
- 目标不能已存在。
- 必须确认。
WriteFileTool
- 修改已有文本文件前自动备份。
- 禁止覆盖二进制文件。
- 禁止写敏感文件。
- 禁止写危险后缀。
- 必须确认。
备份目录:
<workspaceRoot>/.desktoppet/backups/
备份命名:
relative_path.replace("/", "__") + "." + yyyyMMdd-HHmmss + ".bak"
CopyFileTool
- 只复制单文件。
- 源和目标都必须在工作区内。
- 不覆盖。
- 必须确认。
MoveFileTool
- 只移动单文件。
- 源和目标都必须在工作区内。
- 不覆盖。
- 必须确认。
RenameFileTool
- 只允许同目录重命名。
- 文件名不能包含路径分隔符。
- 不覆盖。
- 必须确认。
DeleteToTrashTool
- 只移动到系统回收站。
- 不永久删除。
- 单文件删除第一阶段可支持。
- 文件夹删除需要强确认。
- 批量删除第一阶段拒绝。
9. WorkspaceAgent
第一版采用半自动 Agent,不做复杂工具循环。
推荐流程:
用户输入
↓
WorkspaceController 判断工作区任务
↓
程序根据关键词和索引检索相关文件
↓
读取少量相关文件
↓
把文件树摘要、相关文件内容、用户问题交给模型
↓
模型返回分析结果或修改建议
↓
涉及写操作时生成 WorkspaceActionPlan
↓
用户确认
↓
执行工具
工作区专用系统提示词:
你是 QtDesktopPet 的本地工作区助手。
你只能基于用户授权的工作区文件进行分析。
你不能假设自己看过未读取的文件。
你不能要求访问工作区外路径。
你不能执行系统命令。
你不能生成危险操作指令。
涉及创建、修改、移动、重命名、删除文件时,必须先生成清晰的操作计划,等待用户确认。
涉及敏感文件、密钥、凭证时,必须提醒风险。
回答代码问题时要引用具体文件路径。
当信息不足时,说明还需要读取哪些文件。
后续可升级为工具循环,但第一版不做。
10. WorkspaceController
建议接口:
class WorkspaceController
{
public:
explicit WorkspaceController();
bool hasWorkspace() const;
QString workspaceRoot() const;
bool openWorkspace(const QString &rootPath, QString *errorMessage = nullptr);
void closeWorkspace();
WorkspaceToolResult scanWorkspace();
WorkspaceToolResult handleMessage(const QString &message);
bool hasPendingPlan() const;
WorkspaceActionPlan pendingPlan() const;
WorkspaceToolResult confirmPendingPlan();
void cancelPendingPlan();
private:
WorkspaceSession m_session;
WorkspaceAgent m_agent;
std::optional<WorkspaceActionPlan> m_pendingPlan;
};
原则:
handleMessage()不直接执行高风险写操作。- 需要确认时保存到
m_pendingPlan。 - UI 调用
confirmPendingPlan()后才执行。 - 所有错误通过
WorkspaceToolResult返回,不在工具层弹窗。
11. UI 接入
聊天入口支持:
打开工作区
选择工作区
当前工作区
扫描工作区
分析这个项目
搜索当前工作区
读取 main.cpp
打开第一个
如果没有选择工作区,提示用户选择目录。
气泡只显示短状态:
已打开工作区。
正在扫描文件。
找到了 12 个相关文件。
需要你确认修改计划。
操作已完成。
复杂结果不全部塞进气泡,当前通过独立 Agent 窗口展示。
当前独立 Agent 窗口可显示:
- 当前工作区路径。
- 文件树。
- 搜索结果。
- 已读取文件。
- AI 分析结果。
- 待确认操作计划。
- 执行结果。
- 最近操作历史。
12. 意图分发
后续建议新增:
enum class UserIntentType
{
Chat,
Reminder,
Weather,
Workspace,
FileOperation,
LaunchApp
};
意图原则:
Reminder保持最高优先级。- 明确天气问答走
Weather。 - 明确工作区、代码、项目分析走
Workspace。 - 当前阶段,未明确“工作区/项目/代码”且没有工作区文件名特征的普通文件读写请求仍可走旧
FileOperation。 - 明确应用名启动走
LaunchApp。 - 其余走
Chat或输入框联网模式。
不能用宽关键词简单吞掉所有请求:
- “打开微信”应走
LaunchApp。 - “打开文件夹”这种泛请求不走
Workspace;“打开 docs / 打开这个项目里的 docs”走Workspace。 - “搜索 Qt 最新版本”不应走工作区搜索。
- “搜索这个项目里的 main”应走工作区搜索。
13. 迁移步骤
- 新增
src/workspace/,不改旧src/fileops/。 - CMake 登记 workspace 新文件。
PetWindow增加WorkspaceController成员。- 新增工作区打开、扫描、搜索、读取入口。
- v1b 支持打开和定位;写操作计划和确认框从后续阶段开始。
- 新模块覆盖旧文件操作能力后,
handleFileOperationChatMessage()转发到WorkspaceController。 - 确认无旧引用后删除
src/fileops/。 - 更新 CMake、README、测试文档。
删除旧文件前必须执行:
rg "FileOperationManager|FileSandbox|FileBackupManager|src/fileops|FileOperation"
确认没有有效代码引用后再删。
14. 日志要求
允许记录:
- 打开工作区路径。
- 扫描文件数量。
- 执行的工具名称。
- 操作是否成功。
- 错误摘要。
禁止记录:
- API Key。
- Authorization Header。
- 完整敏感文件内容。
.env内容。- 密钥文件内容。
- 用户完整隐私文本。
使用项目已有 Logger,不要新增零散调试输出。
15. 测试清单
工作区选择
- 选择普通项目目录。
- 选择空目录。
- 选择不存在目录。
- 选择
C:/Windows,应拒绝。 - 选择
Program Files,应拒绝。 - 选择中文路径。
- 选择包含空格的路径。
扫描
- 扫描普通 Qt 项目。
- 扫描包含 build 目录的项目。
- 扫描包含
.git的项目。 - 扫描大量文件目录。
- 扫描中文文件名。
- 扫描深层目录。
要求:
- 不崩溃。
- 不扫系统目录。
- 不进入跳过目录。
- 结果数量受限制。
搜索文件
- 搜索
main。 - 搜索
.cpp。 - 搜索
README。 - 搜索不存在文件。
- 搜索中文文件名。
要求:
- 最多返回 30 条。
- 显示相对路径。
- 结果可用于后续“打开第几个”。
搜索文本
- 搜索
class。 - 搜索
include。 - 搜索中文关键词。
- 搜索不存在关键词。
- 搜索超大文件。
要求:
- 显示路径、行号、片段。
- 跳过二进制文件。
- 跳过超大文件。
- 不读取敏感文件。
读取文件
- 读取
.cpp。 - 读取
.md。 - 读取
.json。 - 读取图片应拒绝。
- 读取
.exe应拒绝。 - 读取
.env应拒绝或强确认。 - 读取工作区外文件应拒绝。
创建文件
- 在工作区创建
test.txt。 - 创建已存在文件应拒绝。
- 创建非法文件名应拒绝。
- 创建
.bat应拒绝。 - 创建到工作区外应拒绝。
修改文件
- 修改普通
.txt。 - 修改
.cpp。 - 修改前生成备份。
- 修改二进制文件应拒绝。
- 修改敏感文件应拒绝。
要求:
- 必须确认。
- 必须备份。
- 失败不能破坏原文件。
移动和重命名
- 移动普通文件。
- 移动到已存在目标应拒绝。
- 重命名普通文件。
- 重命名为非法文件名应拒绝。
- 移动到工作区外应拒绝。
删除到回收站
- 删除普通文件到回收站。
- 删除文件夹到回收站。
- 删除工作区外文件应拒绝。
- 永久删除请求应拒绝。
- 批量删除请求第一阶段应拒绝。
对话上下文
用户:搜索 README
用户:打开第一个
用户:搜索 fileops
用户:读取第二个
用户:找一下包含 FileOperationManager 的文件
用户:总结这些文件的作用
要求:
- 系统能记住上一轮搜索结果。
- “第一个”“第二个”能正确解析。
- 上下文过期后要求用户重新选择。
16. 静态推演要求
每完成一个阶段,需要记录:
- 入口路径。
- 业务模块调用链。
- 路径校验点。
- 用户确认点。
- 失败回滚策略。
- 不支持能力的拒绝话术。
- 对提醒、天气、联网模式、应用启动的回归影响。
17. 最终验收标准
本轮重构完成后至少达到:
- 用户可以选择一个本地项目目录作为工作区。
- 程序可以扫描工作区并建立文件索引。
- 用户可以通过对话搜索文件。
- 用户可以通过对话读取和分析文本文件。
- 用户可以要求分析项目结构。
- 用户可以创建文本文件和文件夹。
- 用户可以移动、重命名、删除到回收站。
- 所有写操作必须展示计划并等待确认。
- 所有路径必须限制在工作区内。
- 不允许永久删除。
- 不允许执行脚本或命令。
- 不允许默认读取敏感文件。
- 旧文件操作功能被新 Workspace 系统替代。
- 项目可以由用户手动构建通过。