AI 改代码总爱顺手重构?一份 Task Contract 把修改范围锁住

一次"小修改",为什么会变成大型Review?

给AI一个看起来很简单的任务:

把订单接口里的 user_name 改成 display_name,并完善相关代码。

几分钟后,功能可能已经跑通,但打开Git Diff会发现:

  • 接口类型改了;
  • 调用方改了;
  • import顺序被重新整理;
  • 几个旧函数被顺手重构;
  • 无关文件被格式化;
  • 依赖版本发生变化;
  • 测试目录也被重新组织。

AI可能没有明显写错代码,Review成本却远远超过任务本身。

这类情况很难只靠一句"不要修改无关文件"解决。

因为在缺少明确边界时,AI需要自行判断"相关代码"包括哪些文件。它可能认为顺手统一风格、清理旧实现和补全类型,都是完成任务的一部分。

对开发者来说,这是越界。

对Agent来说,它可能只是在积极完成一个描述模糊的需求。

项目Rule解决不了所有任务边界

Cursor Rules、AGENTS.mdCLAUDE.md很适合保存长期规则:

diff 复制代码
- 新代码统一使用 TypeScript
- 业务逻辑放在 service 层
- 数据库字段使用 snake_case
- 修改后运行 lint 和单元测试
- 不删除已有测试

这些规则能告诉AI项目长期采用什么风格,却无法完整描述某一次任务:

  • 本次只允许修改哪两个文件?
  • 哪些公共接口不能变化?
  • 是否允许新增依赖?
  • 修改超过多少文件时需要停止?
  • 测试失败与本任务无关时应该怎么办?

长期规则负责回答"这个项目通常怎么开发"。

Task Contract负责回答"这一次具体允许做什么"。

二者不应该混在一起。

Task Contract应该包含什么?

我会把单次任务合同放在:

bash 复制代码
docs/ai-tasks/TASK-2026-0702.md

一个够用的合同至少包含:

markdown 复制代码
# Task Contract

## Goal

修复 coupon 为 null 时订单金额计算失败的问题。

## Current Behavior

calculateDiscount() 在 coupon 为 null 时读取 coupon.rate,
触发 TypeError,接口最终返回 500。

## Expected Behavior

coupon 为 null 时不应用优惠,返回原始订单金额。

## Allowed Files

- src/domain/order/discount.ts
- tests/domain/order/discount.test.ts

## Forbidden Changes

- 不修改数据库结构
- 不修改 API 返回格式
- 不升级或新增依赖
- 不修改其他订单模块
- 不执行全局格式化
- 不删除、跳过或弱化已有测试

## Acceptance Criteria

- coupon 为 null 时返回原始金额
- 有效优惠券逻辑保持不变
- 过期优惠券测试继续通过
- 新增 null coupon 测试
- 不出现 Allowed Files 之外的变更

## Required Validation

npm test -- tests/domain/order/discount.test.ts
npm run lint
npm run typecheck

## Diff Budget

- 最多修改 2 个文件
- 预计变更不超过 80 行

## Stop Conditions

出现以下情况时停止修改并请求确认:

- 必须修改允许列表之外的文件
- 需要调整公共接口
- 需要新增依赖
- 现有实现与任务描述不一致
- 测试失败原因与本任务无关
- 无法在不改变业务规则的情况下完成

这里最重要的并不是Goal

普通Prompt通常已经写了目标,真正缺少的是允许范围、禁止改动和停止条件。

让AI先交计划,不要马上写代码

合同准备好后,先让Agent进入只读阶段:

markdown 复制代码
阅读项目规则和 docs/ai-tasks/TASK-2026-0702.md。

当前阶段只做分析:

- 不修改文件;
- 不创建文件;
- 不运行具有写入效果的命令。

请输出:

1. 根因判断;
2. 计划修改的文件;
3. 每个文件的修改内容;
4. 需要新增或调整的测试;
5. 计划执行的验证命令;
6. 是否存在超出Task Contract范围的风险。

如果需要修改Allowed Files之外的文件,停止并说明原因。

这一步可以在代码产生之前发现范围问题。

假设AI的计划中突然出现:

css 复制代码
src/api/order-controller.ts
src/shared/money.ts
package.json

说明实际任务与合同不一致。

此时应该重新判断:

  • 原合同是否漏掉必要文件?
  • 当前实现方案是否扩大了影响面?
  • 是否应该把任务拆成两个PR?
  • 有没有不修改公共模块的实现方式?

先讨论这些问题,比修改完成后再回滚更省时间。

确认计划后,再开放修改权限

计划通过后,再发第二条指令:

diff 复制代码
按照已经确认的计划执行Task Contract。

要求:

- 只能修改Allowed Files;
- 不得突破Forbidden Changes;
- 保持最小变更;
- 完成后运行Required Validation;
- 输出实际变更文件、测试结果和剩余风险。

一旦触发Stop Conditions,立即停止,不要自行扩大范围。

这样做的核心不是把Prompt拆成两条,而是把"思考"和"执行"分开。

css 复制代码
```mermaid
flowchart LR
    A[读取任务合同] --> B[只读分析]
    B --> C{计划是否越界}
    C -->|是| D[停止并请求确认]
    C -->|否| E[人工确认计划]
    E --> F[执行最小修改]
    F --> G[运行验证]
    G --> H[检查Git Diff]
```

如果一开始就给Agent完整写权限,它可能在分析过程中同步修改文件。等开发者发现计划有问题时,工作区已经产生大量变化。

Diff Budget不是代码质量指标

合同中写了:

复制代码
最多修改2个文件
预计变更不超过80行

这不是要求AI为了控制行数而写出压缩、难懂的代码。

Diff Budget更像一个告警阈值。

任务预计只修改两个文件,结果却出现十几个文件,就应该暂停并解释。确实需要扩大范围时,开发者可以更新合同,而不是让Agent静默继续。

以下情况适合突破原预算:

  • 公共类型确实被多个模块引用;
  • 原测试结构无法覆盖目标行为;
  • 修复暴露了必须同步处理的编译错误;
  • 原需求本身低估了影响范围。

突破预算不可怕。

没有解释地突破预算,才会让Review失控。

修改结束后,用Git检查真实范围

不要直接相信Agent给出的"仅修改了两个文件"。

先看工作区:

lua 复制代码
git status --short

这一步可以发现已修改文件和新建但尚未跟踪的文件。

查看相对当前提交发生变化的已跟踪文件:

css 复制代码
git diff HEAD --name-only

查看变更规模:

bash 复制代码
git diff HEAD --stat

检查空白错误:

css 复制代码
git diff --check

查看完整差异:

复制代码
git diff HEAD

示例任务的预期结果应该只有:

css 复制代码
src/domain/order/discount.ts
tests/domain/order/discount.test.ts

如果出现其他文件,让Agent逐个解释:

diff 复制代码
列出Allowed Files之外的所有工作区变更。

对每个文件说明:

- 为什么发生修改;
- 不修改会导致什么问题;
- 是否可以安全撤销;
- 是否应该拆成独立任务。

当前阶段不要继续编辑。

可以撤销的无关修改应该撤销。

确实必要的修改,则更新Task Contract并重新确认,而不是把它悄悄混进原任务。

测试通过不代表没有越界

一次越界修改完全可能通过测试。

例如,AI为了修复折扣计算,顺手改变了接口错误码。现有单元测试没有覆盖接口协议,CI仍然全绿,但调用方可能已经受到影响。

所以验收需要同时检查:

diff 复制代码
功能结果
+
原有行为
+
修改范围

示例中的验证命令是:

arduino 复制代码
npm test -- tests/domain/order/discount.test.ts
npm run lint
npm run typecheck

具体项目应该替换成真实存在的命令。

如果项目需要数据库、私有API或特定测试环境,还要在合同中明确:

  • 哪些验证可以本地完成;
  • 哪些需要测试环境;
  • 哪些结论依赖人工确认;
  • 哪些代码只是示例结构。

Rule与Task Contract怎么分工?

可以把长期规则写成:

markdown 复制代码
# AI Development Rules

- 修改前必须先输出计划
- 优先最小变更,不主动重构无关代码
- 不得删除测试或降低校验标准
- 每次任务必须读取对应Task Contract
- 触发Stop Conditions时必须停止
- 完成后必须报告实际修改文件和验证结果

工具对应位置可以按项目选择:

sql 复制代码
Cursor      -> .cursor/rules/*.mdc 或 AGENTS.md
Claude Code -> CLAUDE.md
其他Agent   -> 项目支持的仓库指令文件

Cursor官方建议项目规则保持具体、可执行并合理限定范围;Claude Code也建议在CLAUDE.md中保存项目架构、代码规范和常用命令。

Task Contract则不要无限积累到全局规则中。

它只服务一次任务,完成后可以保留为Review依据,也可以在合并后归档。

这套方法适合哪些任务?

特别适合:

  • 修复线上Bug;
  • 修改接口字段;
  • 小范围性能优化;
  • 补单元测试;
  • 调整业务校验;
  • 修改遗留项目;
  • 在不熟悉的仓库中使用AI Agent。

从零搭建原型时,边界可以放宽。

进入已有项目、多人协作仓库和生产代码后,边界应该收紧。

AI能力越强,越需要明确它什么时候必须停手。

结论

项目Rule解决的是长期规范,Task Contract解决的是单次任务边界。

一套比较稳的AI改码流程应该是:

复制代码
定义任务合同
→
AI只读分析
→
人工确认计划
→
执行最小修改
→
运行验证
→
Git核对范围
→
人工Review

下一次让AI修改代码之前,可以先花两分钟写下:

  • 允许改哪些文件;
  • 哪些地方不能动;
  • 如何验证结果;
  • 什么情况下必须停止。

这两分钟不会让AI写得更快,却能让后面的Diff更容易看懂,也更容易安全合并。


参考资料

相关推荐
大志说编程2 小时前
Agent面试真题06: 十分钟带你快速掌握Agent记忆管理高频面试题(附详细答案)
后端·面试·ai编程
葡萄城技术团队2 小时前
从提示词工程到 Harness Engineering:打造坚实可靠的 AI 开发系统
ai编程
用户61635661811042 小时前
手搓AI工作流:让AI从“野马“变“战马“
ai编程
玄星啊2 小时前
AI 编程的第 30 天,我怀念古法 Coding 了
前端·ai编程
唐老板2 小时前
给AI加了3条规则,SQL翻车率降了
ai编程
深蓝AI2 小时前
Claude Code 子智能体实战:让 AI 自己调 AI 来写代码
ai编程
ServBay2 小时前
Claude Code 被曝植入后门,AI 时代如何安全打造本地 DevOps
后端·ai编程·claude
Fanta丶4 小时前
1.VibeCoding 终端命令基础使用
claude
colir04 小时前
被粉丝夸爆的超级 ai 个人工作站,原来这么多福利
开源·agent·claude