Skip to content

[Harness Epic] 分清哪些改动是 Agent 做的,并能安全撤销或处理冲突 #93

Open

Description

@rojim666

这条 Issue 要解决什么

Agent 会直接操作用户的真实项目。当前系统能在任务开始和结束时比较文件变化,也能在文件没有被再次修改时恢复旧内容,但它还不能准确管理一次任务中所有副作用。

真正需要解决的是:

  • 哪个文件、哪一段内容是哪个 Agent 或工具改的;
  • 用户原来未提交的修改是否被保护;
  • Agent 和用户同时改一个文件时如何处理;
  • Shell 命令改了很多文件或启动后台进程时如何记录;
  • 任务崩溃后,哪些操作能撤销,哪些只能人工确认。

用一个实际场景说明

用户的 app.py 原本就有未提交修改。Agent 又修改了同一个文件,并运行 ruff --fix 改动另外 20 个文件。任务随后失败。

用户点击“撤销 Agent 修改”时,系统必须做到:

  • 保留用户原来的修改;
  • 只撤销 Agent 新增的部分;
  • 识别 ruff --fix 修改的 20 个文件;
  • 如果用户之后又编辑了其中一个文件,不用旧快照直接覆盖;
  • 明确说明是否存在无法撤销的外部操作。

当前首尾快照无法完整回答这些问题。

当前实现的具体限制

  • WorkspaceChangeTracker 只在任务开始和结束时扫描整个目录,无法记录每次工具调用分别改了什么;
  • 单文件超过 1 MB、总快照超过 32 MB,以及被忽略目录中的内容会被跳过,但最终记录没有明确告诉用户“本次记录不完整”;
  • write_file 和 edit_file 直接写入真实文件,没有统一的修改事务;
  • Bash 在真实工作区执行,可以修改大量文件、启动子进程或访问外部服务;
  • 当前回退会检查文件是否仍和任务结束时一致,这个保护是正确的,但只能按整个文件恢复,不能处理同一文件中的用户修改和 Agent 修改;
  • Git 暂存区、未跟踪文件、重命名、文件权限、符号链接和二进制文件缺少统一规则。

主要入口:

  • src/sztu_code/core/changes.py
  • src/sztu_code/core/tools/builtin/write_file.py
  • src/sztu_code/core/tools/builtin/edit_file.py
  • src/sztu_code/core/tools/builtin/bash.py
  • src/sztu_code/core/tools/invocation.py
  • src/sztu_code/core/workspace/manager.py

修改后应该是什么样

1. 每次任务有独立的修改账本

对每次可能产生副作用的工具调用,记录:

  • 准备做什么;
  • 得到了什么权限;
  • 执行前文件是什么版本;
  • 实际改了哪些文件;
  • 能不能撤销;
  • 如果进程中断,结果是否确定。

最终 Diff 应该由这些记录组成,而不是只在结束时扫描一次目录。

2. 保护用户原有修改

任务开始时保存用户工作区的基线,包括:

  • Git HEAD;
  • 暂存区;
  • 未暂存修改;
  • 未跟踪文件。

Agent 的改动应叠加在这个基线上。接受或撤销 Agent 改动后,用户原来的状态必须保持不变。

不能使用 git reset --hard、git clean 或自动 stash 来简化实现。

3. 尽可能在隔离工作区执行

Git 项目可以使用临时 worktree 或覆盖层;非 Git 项目可以使用按需复制的临时工作区。Agent、测试和 Shell 优先操作隔离版本,用户接受后再合并回真实项目。

需要注意:只从 Git HEAD 创建 worktree 不够,因为那会漏掉用户当前未提交的修改。隔离工作区必须包含用户开始任务时看到的真实基线。

对于无法隔离、必须直接操作真实项目的任务,要明确提示风险并使用严格的版本检查。

4. 写入前检查文件有没有变化

Agent 读取文件后准备修改时,如果文件已经被用户或另一个 Agent 改过,旧修改不能直接覆盖新内容。

系统应:

  • 能自动合并时进行三方合并;
  • 同一段内容冲突时暂停并展示冲突;
  • 记录冲突来自用户还是另一个子 Agent。

5. Shell 命令也必须进入同一套管理

Shell 不能因为是一个通用工具就绕过修改账本:

  • 执行前记录工作区版本和命令范围;
  • 执行后实际检测文件变化;
  • 子进程属于当前任务,取消时清理整棵进程树;
  • 写出工作区、访问网络、全局安装或发布等能力单独授权;
  • 命令是否执行成功无法确认时,标记为“结果未知”,不能自动重试。

6. 撤销本身也要留下记录

撤销不是删除历史。系统要保留原修改、撤销操作和撤销结果。如果文件已经变化,撤销进入冲突状态,不能悄悄覆盖。

必须保证的规则

  • 用户原有修改永远不能被 Agent 回退操作覆盖。
  • 每个 Agent 修改都能追溯到具体 run、子 Agent 和工具调用。
  • Diff、测试结果和用户点击接受必须针对同一个文件版本。
  • 文件又被修改后,旧测试结果和旧审核结论自动失效。
  • 无法确认是否执行成功的外部操作,必须显示“结果未知”。
  • 无法完整记录的大文件或外部目录不能被静默忽略。
  • 子 Agent 可以分别工作,但合并时必须检测冲突。

建议分阶段实现

第一阶段:修改账本和文件版本检查

  • 将首尾快照升级为按工具调用记录的修改账本;
  • write/edit 工具统一通过 WorkspaceTransaction 写入;
  • 保存用户脏工作区基线;
  • 支持安全的文件级接受和撤销。

第二阶段:隔离工作区和 Shell 管理

  • 实现 Git worktree/覆盖层和非 Git 临时工作区;
  • 监控 Shell 实际产生的文件变化;
  • 管理子进程树、越界写入和结果未知状态;
  • daemon 重启后恢复未结束的修改记录。

第三阶段:局部审核和并发合并

  • 支持按文件、按修改片段接受或撤销;
  • 支持用户与 Agent、多个子 Agent 的三方合并;
  • 把测试和审核结果绑定到具体工作区版本;
  • 在桌面端和 TUI 显示修改来源、冲突和不可撤销操作。

验收标准

  • 在同时存在 staged、unstaged 和 untracked 文件的项目中运行 Agent,接受或撤销后,用户原有状态逐字节不变。
  • write/edit、Shell、子 Agent 产生的每个文件变化都能找到来源。
  • 用户在 Agent 完成后修改同一文件,再点击撤销时,系统提示冲突,不覆盖新内容。
  • 在文件创建、删除、重命名和写入过程中强制结束 daemon,重启后能明确判断已完成、未执行、冲突或结果未知。
  • 大文件、二进制文件、文件权限和符号链接不会被静默漏记。
  • Shell 启动的后台子进程在取消后被清理;无法清理时明确报告。
  • 任意新修改都会让旧版本的测试证据失效。
  • 10 万文件仓库有性能测试,不能每次写文件都重新扫描整个项目。

维护者备注

底层可以把一次工具副作用分为“准备、执行、观察、确认、补偿”几个状态。这里不要求对网络发布或数据库写入实现万能回滚;正确做法是承认哪些操作不可逆,并通过权限、查询接口和人工确认管理风险。

Activity

  1. changed the title [-][Harness Epic] 构建工作区事务层:隔离 Agent 修改、保护用户改动并支持安全回滚[/-] [+][Harness Epic] 建立 Effect Kernel:工作区事务、外部副作用账本与补偿恢复[/+] on Aug 13, 2026
  2. changed the title [-][Harness Epic] 建立 Effect Kernel:工作区事务、外部副作用账本与补偿恢复[/-] [+][Harness Epic] 分清哪些改动是 Agent 做的,并能安全撤销或处理冲突[/+] on Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions