☰
SWE-Touch:用户改过代码后,AI编程助手还能继续干活吗?
2026/10/28 20:18:50 网站建设 项目流程

平时用 AI 编程助手改代码时,我经常陷入一种很尴尬的流程:Agent 改到一半,我会忍不住自己上手微调几行逻辑,比如改个参数默认值、补一个边界判断,或者把函数签名调整一下。结果再让 Agent 继续干活时,它就像短暂失忆了一样,要么重复生成已经被我改掉的代码,要么在我刚改过的地方继续“叠床架屋”。

这种“用户中途触摸代码”的协作场景,在真实开发里非常常见,但在主流 Coding Agent 评测体系里却长期缺位。SWE-Touch 这个基准测试(Benchmark)正是冲着这个问题来的:当用户已经亲手改过代码之后,编码智能体还能不能保持正确的理解和判断?

本文会围绕 SWE-Touch 的评测思想展开,先讲清楚它和传统编码智能体评测的差异,再拆解真实协作中的挑战,最后用一个最小可运行的 Python 示例,演示一套简化版评测闭环。适合正在做 Coding Agent 应用开发、或者准备给团队接入 AI 编程助手的后端开发者阅读。

1. 背景与核心概念:从 SWE-bench 到 SWE-Touch

1.1 编码智能体评测的主流范式

先来回顾一下 Coding Agent 评测的主流做法。以大家熟悉的 SWE-bench 类基准测试为例,它的评测流程大致是这样的:给定一个真实 GitHub Issue,再给一个完整代码仓库的固定快照,让编码智能体独立完成问题定位、代码修复、生成补丁,最后用仓库自带的测试集验证补丁是否让相关用例通过。

这种“Issue → Patch → Test”的闭环设计,优点是清晰、可复现、可横向比较。不同 Agent 面对同样的任务,最后看通过率就能分出高下,因此它很快成为编码智能体领域最常用的评测范式。

但它隐含了一个很强的前提假设:Agent 是在一份“静止的、无人打扰的代码”上工作的。任务一开始,仓库状态就是最终状态,Agent 只需要从零开始理解这份代码,然后独立完成修改,不需要考虑别人是不是已经动过代码。

这个假设在大规模评测时是合理的,因为它保证了所有 Agent 面对完全相同的起点。但放到真实开发环境里,这个假设经常不成立。

1.2 用户触摸代码:真实协作中被低估的变量

现实中的 AI 辅助编程,从来不是“Agent 独自完成全部工作”的孤岛模式,而是一种高度频繁的人机协作。用户会根据自己对业务逻辑的理解,随时插入到代码修改过程中。

常见的有三种情况:

  • 用户先改,Agent 后做:用户已经手动实现了一部分功能,剩下的交给 Agent 补齐。
  • Agent 做到一半,用户插进来:Agent 正在生成代码或补丁的过程中,用户自己改了相关文件,Agent 需要基于新的状态继续。
  • Agent 改完,用户 reivew 后又改:Agent 产出了修复补丁,用户 review 时手动调整了实现方式或 API 名称,后续 Agent 还要继续维护。

这三种情况里,代码状态已经不是“初始快照”,而是被用户动过的“中间状态”。用户对代码的每一次修改,都可能是新增约束、调整接口、修复一部分 bug,也可能无意中留下新的问题。

SWE-Touch 这个名字里,“Touch”指的就是用户亲手触摸、修改代码这个动作。它把这类场景纳入了基准测试的核心设计,考察 Agent 在用户已改过代码之后,是否还能正确感知变更、理解用户意图,并在此基础上继续高质量地完成编码任务。

1.3 SWE-Touch 的核心评测视角

从评测方法论上看,SWE-Touch 与传统基准测试最大的区别在于:它不再只关注“Agent 能否独立从零生成正确补丁”,而是关注“Agent 能否在一个已经被用户修改过的代码状态下,继续完成剩余工作”。

这听起来只是一点任务构造差异,实际上会引出一系列更复杂的评测维度:

  • 变更感知:Agent 能不能主动发现用户改过哪些文件,而不是两眼一抹黑地基于旧理解输出代码。
  • 意图对齐:用户手动改代码往往带有明确的业务意图,Agent 能不能从改动中反推这些意图,并保持对齐。
  • 冲突处理:Agent 要补充的实现,会不会和用户已经写好的部分产生冲突,会不会把用户刚写的代码“反向改回去”。
  • 回归判断:用户改过之后,原本能通过的测试是否仍然安全,Agent 有没有能力在不破坏用户修改的前提下完成剩余工作。

换句话说,SWE-Touch 想评测的不是“Agent 的代码能力”,而是“Agent 在真实协作场景中的可持续工作能力”。

2. 现实场景中的协作模式与挑战

2.1 三种典型的用户触摸模式

为了把问题讲清楚,我们可以把用户触摸代码的行为归纳成三种典型模式。

模式发生时机用户行为Agent 面临的典型任务
先手触摸Agent 开始工作之前用户已手动改好一部分代码,留下 TODO 或半成品理解已有改动,补齐剩余逻辑
中途插入Agent 工作过程中用户直接修改了 Agent 正在处理的文件重新读取最新状态,避免重复或冲突
后置修改Agent 产出补丁之后用户 review 并手动调整了 Agent 的改动基于新改动继续迭代,不能回退用户调整

在传统评测里,这三种模式基本没有体现。任务一开始仓库就是“干净”的,Agent 不会意识到中途会有人插手,也不需要理解“用户意图”这种偏主观的变量。

2.2 用户修改给 Agent 带来的四类困难

当用户真的摸过代码之后,Agent 拿到的不再是一个单纯的技术问题,而是一个混合了“代码理解问题”和“协作对齐问题”的复杂任务。

第一类是信息过时。真实项目里用户可能直接编辑文件,也可能通过 IDE 重构、批量替换等方式改了多个文件。Agent 如果只依赖刚才上下文里的代码片段,而不是重新读取当前文件内容,就会基于过时信息生成代码。结果是代码合并后出现重复定义、接口错位甚至语法错误。

第二类是隐含约束。用户手动改代码往往不是随便改的,背后通常有业务约定。比如把某个参数默认值从False改成True,可能意味着后续所有调用方都希望走四舍五入逻辑。Agent 如果看不见这个改动背后的意图,就很难写出符合预期的实现。

第三类是目标漂移。用户中途插入修改,可能让原始任务的边界发生变化。比如原本只是让 Agent 修一个计算 bug,用户顺手把函数签名改了,Agent 再继续“修 bug”时,必须先把签名调整考虑进去,否则所有修法都是白做。

第四类是变更回退风险。最糟糕的不是 Agent 不会改,而是 Agent 生成的补丁把用户刚写好的代码又覆盖回去了。这种问题在自动化评测里很难自动发现,因为测试可能照样通过,但用户实际体验是灾难性的。

2.3 为什么传统 benchmark 无法覆盖这些情况

传统基准测试之所以没有覆盖这些情况,根因在于它把“任务”定义得太线性了。任务只有一个起点、一个终点,Agent 从头走到尾即可。

真实开发中,任务更像是“状态流”:初始仓库状态 → 用户手动修改 → Agent 继续修改 → 用户 review 修改 → Agent 再次修改。每一步都可能产生新的状态,Agent 必须随时响应状态变化。

此外,传统评测的判定标准通常只有“测试是否通过”,它不太关心 Agent 是否破坏了用户已有的修改。SWE-Touch 这类基准测试的出现,本质上是在推动评测标准从“结果正确性”转向“协作正确性”,让 Benchmarking 更贴近真实工程实践。

3. 环境准备与最小评测框架搭建

理解了背景之后,我们来动手搭一个最小评测框架。这个框架会模拟 SWE-Touch 的核心思路:仓库处于“用户触摸后”的中间状态,Agent 需要感知这个状态,并完成剩余开发任务。

3.1 运行环境建议

本文的示例以常见的 Python 环境演示,重点讲解评测闭环的设计思路,不依赖特定云服务或复杂基础设施。

需要注意,具体版本需要根据你的项目实际情况调整。建议准备:

  • Python 3.10 或更高版本;
  • pytest 测试框架;
  • git 命令行工具;
  • 一个终端或 IDE 内置终端。

3.2 项目目录结构

我们先创建一个名为swetouch-demo的目录,模拟一个最小仓库。

mkdir -p swetouch-demo/app swetouch-demo/tests cd swetouch-demo python -m venv .venv source .venv/bin/activate pip install pytest git init

Windows 环境下,虚拟环境激活命令稍有不同,需要改为:

.venv\Scripts\activate

最终目录结构如下:

swetouch-demo/ ├── app/ │ ├── __init__.py │ └── pricing.py ├── tests/ │ └── test_pricing.py └── eval_runner.py

3.3 准备一个最小的示例仓库

为了让示例足够聚焦,我们模拟一个电商场景中的折扣计算函数。业务目标很简单:给定商品原价和折扣比例,计算折后价,并支持一个“是否四舍五入到分”的开关。

先创建app/__init__.py,内容可以留空,只是为了把app变成可导入的 Python 包:

touch app/__init__.py

现在准备“用户触摸之后”的代码状态。

4. 核心原理:如何构造 SWE-Touch 风格评测任务

在写代码之前,先理解评测任务是怎么构造的。SWE-Touch 风格的评测任务,本质上是一个带“中间状态”的状态机。

4.1 任务状态流

我们可以把一次评测任务拆成四个阶段:

初始仓库(Start) → 用户手动修改(User Touch) → Agent 生成补丁(Agent Patch) → 运行测试验证(Test Result)

第一阶段是初始仓库,对应传统基准里的原始代码状态。第二阶段会模拟用户对代码的手动修改,这是 SWE-Touch 任务最核心的部分。第三阶段让 Agent 基于用户修改后的状态继续工作。第四阶段用测试集验证最终结果。

需要特别强调的是,用户手动修改不是简单地在代码里“埋一个 bug”,而是模拟真实协作中的部分实现、接口调整或约束新增。用户的改动本身可能是有意义的开发进度,Agent 不能无视它。

4.2 评测指标设计

在 SWE-Touch 风格的评测里,指标设计比传统测试通过率更丰富一些。通常可以关注以下四类指标:

第一,测试通过率。这是最基础的指标,决定 Agent 的补丁是否让任务功能上正确。

第二,用户变更保持率。Agent 生成的补丁是否保留用户手动修改的部分,有没有出现“把用户代码改回去”的情况。

第三,迭代效率。Agent 是花了一步就理解了用户改动,还是反复生成大量无效补丁后才摸清状态。

第四,变更定位能力。Agent 能否准确识别用户改过的文件,而不是在无关文件里做无用功。

在本文的最小示例中,我们主要以测试通过率作为可验证指标,其余指标可以在真实评测平台中扩展实现。

4.3 构造任务时的注意事项

构造这类评测任务时,有几个细节容易踩坑。

一是用户修改不能太随意。用户的改动要有合理性,比如补充了部分参数校验、预留了接口,而不是毫无逻辑地删代码。

二是测试用例要区分“原有约束”和“用户新增约束”。最好用注释或显式标记说明哪些测试是用户触摸之后新增的,这样 Agent 能更清楚用户改了什么。

三是必须提供显式的“当前状态”。真实开发中用户改了代码一般会留下工作区变更,Agent 可以通过git diff感知差异。评测框架也应该把这种感知能力纳入考量。

5. 模拟实战:用 Python 实现一个简化版 SWE-Touch

接下来进入完整实战环节。我们会手动构建一个“用户触摸后”的代码状态,并写一个评测脚本,模拟 Agent 从当前状态继续完成任务的完整流程。

5.1 定义“用户已触摸”的代码状态

先创建app/pricing.py。这个文件是用户手动修改后的版本,也是 Agent 开始工作前看到的起点。

# 文件路径:swetouch-demo/app/pricing.py # 当前状态:用户手动修改后的版本(Agent 看到的起点) def calculate_discount(price: float, discount: float, round_result: bool = True) -> float: """计算折后价格。 参数说明: price : 商品原价,必须大于等于 0 discount : 折扣比例,范围 0 ~ 100 round_result : 是否将最终结果四舍五入到两位小数 """ if price < 0: raise ValueError("price 不能为负数") if discount < 0 or discount > 100: raise ValueError("discount 必须在 [0, 100] 区间") result = price * (1 - discount / 100) # TODO(agent): 请根据 round_result 参数决定是否对结果四舍五入 # 用户已经完成了参数校验和基础折扣计算,剩余逻辑由 Agent 补充。 return result

注意,这个代码是“半成品”。用户已经完成了参数校验和基础折扣计算,但round_result参数还没有生效。Agent 的任务就是读取这个函数,理解用户已经完成的部分,然后补齐剩余逻辑。

这里面没有埋所谓的低质量 bug,反而更贴近真实协作:用户做了一部分,剩下的交给 Agent。

5.2 加入用户补充的测试用例

接着创建测试文件tests/test_pricing.py。其中前两个用例是原有约束,后两个用例是用户触摸后新增的约束。

# 文件路径:swetouch-demo/tests/test_pricing.py import pytest from app.pricing import calculate_discount def test_normal_discount(): """基础折扣场景。""" assert calculate_discount(100, 20) == 80.0 assert calculate_discount(100, 0) == 100.0 def test_full_discount(): """100% 折扣时价格应为 0。""" assert calculate_discount(100, 100) == 0.0 def test_round_result_true(): """用户补充用例:默认对结果四舍五入到分。""" assert calculate_discount(99.99, 10, round_result=True) == 89.99 def test_round_result_false(): """用户补充用例:关闭四舍五入时保留原始精度。""" assert calculate_discount(99.99, 10, round_result=False) == 89.991

这里有两个非常关键的细节。

第一,test_round_result_true和test_round_result_false是用户触摸之后新增的用例,它们明确了用户对round_result参数的约束。

第二,如果 Agent 没有感知到用户已经新增了这些约束,直接把函数按旧逻辑写死,就无法通过全部测试。

5.3 编写评测脚本

接下来创建eval_runner.py,这是整个评测闭环的入口。它会模拟一个 benchmark 系统:应用 Agent 提交的补丁,然后运行测试集,根据结果判定任务是否成功。

# 文件路径:swetouch-demo/eval_runner.py """简化版 SWE-Touch 评测脚本。 它演示了 benchmark 的核心闭环: 1. 仓库处于“用户触摸后”的中间状态; 2. Agent 提交一个补丁文件; 3. 评测系统应用补丁并运行测试; 4. 根据测试结果判定任务是否成功。 """ import argparse import subprocess from pathlib import Path REPO_DIR = Path(__file__).resolve().parent TEST_CMD = ["python", "-m", "pytest", "tests/", "-q"] def apply_patch(patch_path: Path) -> None: """把 Agent 输出的 patch 应用到仓库。""" result = subprocess.run( ["git", "apply", str(patch_path)], cwd=REPO_DIR, capture_output=True, text=True, ) if result.returncode != 0: raise RuntimeError( f"应用 patch 失败,stderr:\n{result.stderr}" ) print(f"==> 已应用 patch: {patch_path.name}") def run_tests() -> bool: """运行单元测试,返回是否全部通过。""" result = subprocess.run( TEST_CMD, cwd=REPO_DIR, capture_output=True, text=True, ) print(result.stdout) if result.stderr: print(result.stderr) return result.returncode == 0 def main() -> None: parser = argparse.ArgumentParser(description="SWE-Touch 简化评测脚本") parser.add_argument( "--patch", type=Path, default=Path("agent.patch"), help="Agent 生成的 patch 文件,默认使用当前目录下的 agent.patch" ) args = parser.parse_args() if not args.patch.exists(): raise FileNotFoundError(f"找不到 patch 文件: {args.patch}") print("==> 步骤 1: 应用 Agent patch") apply_patch(args.patch) print("==> 步骤 2: 运行测试集") passed = run_tests() print(f"==> 任务结果: {'PASSED' if passed else 'FAILED'}") if not passed: raise SystemExit(1) if __name__ == "__main__": main()

这个脚本虽然简单,但已经具备了一个 benchmark 评测器的基本骨架:接收补丁、应用补丁、执行验证、输出结果。真实评测平台会在此基础上增加沙箱隔离、超时控制、多任务批量执行等能力。

5.4 运行与验证

先不应用任何补丁,直接运行测试,能够看到当前“用户触摸后”的状态并不能通过全部用例。

cd swetouch-demo python -m pytest tests/ -q

预期输出大概是这样:

..FF ================================== FAILURES ================================== ___________ test_round_result_true ____________ ... ___________ test_round_result_false ____________ ... 2 failed, 2 passed in 0.05s

两个原有用例通过,两个用户新增用例失败。这说明 Agent 必须继续工作,补齐round_result的处理逻辑。

现在模拟 Agent 的工作成果。创建一个补丁文件agent.patch:

diff --git a/app/pricing.py b/app/pricing.py --- a/app/pricing.py +++ b/app/pricing.py @@ -13,6 +13,8 @@ def calculate_discount(price: float, discount: float, round_result: bool = True) result = price * (1 - discount / 100) - # TODO(agent): 请根据 round_result 参数决定是否对结果四舍五入 - # 用户已经完成了参数校验和基础折扣计算,剩余逻辑由 Agent 补充。 - return result + if round_result: + result = round(result, 2) + + return result

然后运行评测脚本:

python eval_runner.py --patch agent.patch

预期输出:

==> 步骤 1: 应用 Agent patch ==> 已应用 patch: agent.patch ==> 步骤 2: 运行测试集 .... [100%] 4 passed in 0.02s ==> 任务结果: PASSED

到这里,一个简化版的 SWE-Touch 评测闭环就跑通了。

5.5 从模拟到真实 Benchmark

需要提醒的是,上面这个示例是教学级模拟,目的是帮助你理解 SWE-Touch 的评测思想。真实的 SWE-Touch 基准测试会复杂得多,主要体现在几个方面。

任务规模更大。真实基准会准备大量真实仓库、真实 Issue 和真实用户修改,形成完整的任务集,而不是单独一个函数。

用户修改更贴近真实历史。真实用户修改可能涉及多个文件、重构、注释调整、测试补充等,Agent 需要依赖工具感知变更,而不只是读一个文件。

评测流程更工程化。包括 Docker 沙箱、资源限制、超时控制、防止 Agent 作弊、统一补丁校验、多轮交互日志记录等。

不过,把最小闭环跑通之后,再去阅读真实基准的评测代码和任务构造逻辑,会轻松很多。

6. 常见问题与排查思路

在搭建和运行这类评测闭环时,比较容易遇到一些共性问题。这里整理成一份排查清单。

问题现象常见原因解决思路
应用 patch 失败,报patch does not apply仓库状态和 patch 基准不一致,或 Agent 基于旧状态生成补丁确认仓库处于用户触摸后的状态;要求 Agent 先查看git diff再生成补丁
测试结果不稳定,同一补丁时而过时而不通过测试用例存在随机性或依赖外部服务在评测逻辑中固定随机种子、隔离网络、使用沙箱环境
Agent 生成的补丁把用户修改覆盖了Agent 没有感知用户改动,基于旧代码上下文输出在提示词中强制 Agent 先读取当前文件,再输出补丁;评测时增加“用户变更保持率”指标
评测时仓库状态被污染上一次测试的补丁没有回滚在评测脚本执行前重置仓库,回到“用户触摸后”的状态
不同 Agent 结果无法横向对比评测基准不统一、测试集不一致固定同一份 task 集和测试集,统一超时和资源限制
Agent 反复尝试但一直不通过用户新增约束没有被 Agent 理解检查用户改动是否足够清晰;真实场景中可要求 Agent 先输出对用户改动的理解

在这类问题里,最容易被忽视的是“仓库状态被污染”。尤其是本地手工实验时,上一次git apply的补丁如果没回滚,下一次评测结果就会失真。建议在评测脚本里增加一个 reset 步骤,每次都从干净的“用户触摸状态”出发。

7. 最佳实践与工程建议

7.1 对 Coding Agent 开发者的建议

如果你在开发自己的 Coding Agent,SWE-Touch 的核心思想可以直接落到产品设计里。

让 Agent 具备“先读差异再动手”的习惯。当 Agent 进入一个仓库时,首先应该执行git diff、git status之类的能力,明确当前工作区有哪些用户改动,再基于最新状态规划修改方案。

给 Agent 提供“用户改动摘要”的接口。对于已有的代码改动,可以通过工具或者预处理的变更摘要,把用户修改的文件、函数、约束条件注入到上下文中。这能显著降低 Agent 对用户意图的误解。

在评测链路中加入“用户改动保持检查”。简单做法是在评测脚本中,先保存用户改动文件的内容摘要,Agent 输出补丁后,检查这些文件的核心改动是否仍然存在。如果被 Agent 反向回退,即便测试通过,也应判定为失败或降权。

7.2 对 Benchmark 使用者的建议

如果你正在用 SWE-Touch 或类似基准评估编码智能体,建议不要只看整体通过率,重点关注 Agent 在“用户触摸后”的稳定性。

具体来说,可以按维度拆分统计:用户先手修改场景下的通过率、Agent 中途被插入修改时的通过率、Agent 修改后用户再 review 的通过率。三种场景的难度和风险完全不同,混在一起看容易掩盖问题。

同时,尽量让 Agent 在评测中拥有与真实开发一致的工具权限。如果 Agent 没有读取git diff的通道,那么在 SWE-Touch 任务里天然处于劣势,这种劣势不能完全代表模型能力。

7.3 对将 Agent 落地到真实项目的团队

对真实项目团队来说,SWE-Touch 提醒我们的其实是工程协作设计问题。团队在引入 AI 编程助手时,应该为“人机协作改码”设定清晰的秩序。

一方面,尽量让 AI 和用户的改动范围解耦。比如约定 AI 优先生成新文件或新增函数,用户手工维护核心业务逻辑,减少同一区域的直接冲突。

另一方面,建立代码变更的强制 review 机制。Agent 产生的 diff,必须经过git diff检视后再入库,不要让 Agent 直接提交。这样即使 Agent 基于过时信息输出了补丁,也能在合并之前被拦截。

在配置 Agent 的权限时,遵循最小权限原则也是必要的。不要让 Agent 直接改动 git 历史、强制推送或批量替换文件,所有高风险操作都应该限制在测试环境或者经过人工确认后执行。

8. 总结与下一步学习方向

这篇文章从一个真实协作痛点出发,介绍了 SWE-Touch 这种基准测试的核心思想:编码智能体不应该只会在干净仓库里完成 Issue 修复,更应该能在用户已经触摸过代码之后继续稳定工作。

我们搭建了一个最小可运行的评测闭环,模拟了“用户手动修改代码 → Agent 继续补齐逻辑 → 测试验证结果”的完整流程。虽然示例规模很小,但评测框架的四个核心阶段——初始状态、用户触摸、Agent 补丁、测试验证——已经完整呈现。

接下来如果你对这个方向感兴趣,可以沿着两条线继续深入。一条是在真实任务集上跑 SWE-Touch,观察不同 Coding Agent 在用户修改场景下的稳定性差异;另一条是改进自己的 Agent 产品,让它具备“感知变更、尊重用户改动、规避冲突”的协作能力。

最后留一个实践题目给你:在这个示例仓库里,如果用户触摸后的代码不是留下 TODO,而是把round_result的处理逻辑写反了,Agent 应该怎样才能在不破坏用户意图的前提下完成任务?建议自己动手跑一遍,会很有收获。

如果你的工作流里也经常出现“AI 改到一半、你又手动改了几行”的情况,欢迎在评论区分享你的场景和踩坑经历。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询