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

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

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

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

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

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

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

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

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

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

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

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

Cursor Rules、AGENTS.md和CLAUDE.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 小时前
花椒 AI 应用工程化实践:灵泡泡的工作流、版本管理与任务恢复
后端·agent·ai编程
OpsEye3 小时前
大量无效 Token 消耗,是很多企业被忽略的隐形成本
javascript·ai编程
jerrywus3 小时前
Codex、Claude Code、DSH 怎么用手机管?我试了试 Agents Anywhere
前端·claude·deepseek
没事写点啥3 小时前
WeKnora 问答实现
后端·agent·ai编程
jarvisuni4 小时前
【无标题】
人工智能·ai编程
孔令飞4 小时前
【OneX】OneX AI 工具安装、配置及功能介绍
云原生·go·ai编程
小虎AI生活4 小时前
用 GPT-6 三天做出手机游戏是什么体验?附零基础四步实操
aigc·ai编程
莪_幻尘4 小时前
10000 个中国版 Codex 在路上:桌面 Agent 争夺执行入口,一篇理清
openai·ai编程
李航19834 小时前
DeepDraw 网页版
人工智能·python·计算机视觉·ai·ai编程
Elcker5 小时前
Ynuo Agent(依诺) 一 0.3.10 发布:研发透明化,抛弃许愿式编程
人工智能·agent·ai编程