AI改代码最麻烦的情况不是不会写而是顺手改了无关文件。本文提供一套“变更合同”方法把目标、允许修改的文件、禁区、验收标准和停止条件提前写清楚再通过Git检查控制修改范围。一个字段改名AI为什么动了18个文件你只是想把接口里的user_name改成display_name。AI读完项目后不仅修改了接口类型和调用位置还顺手做了这些事重排了多个文件的 import格式化了整个目录升级了一个依赖把附近的旧函数改成了新写法给无关模块补了类型重新组织了测试文件。代码可能都能运行但Review已经变得很难。你原本只需要确认一个字段修改现在却要检查18个文件判断每一处变化是否必要。这类问题不能简单归因于“模型不听话”。当任务只写着“帮我把字段改掉并完善相关代码”时AI得到的边界非常宽。为了让结果看起来更完整它可能主动处理所有相关问题。对开发者来说这是无关改动对缺少限制的智能体来说却可能是“顺便把任务做好”。解决办法不是反复补一句“不要乱改”而是为每个编码任务准备一份可检查的变更合同。什么是变更合同变更合同不是法律文件也不是一篇冗长的需求文档。它是一份放在仓库里的任务约束明确告诉AI这次要解决什么问题哪些文件允许修改哪些目录不能碰什么结果才算完成必须执行哪些验证什么时候应该停止并询问开发者。相比一段临时聊天文件化的约束更容易复用、审查和纳入版本管理。项目已经使用Cursor Rules、AGENTS.md或CLAUDE.md时可以把长期规则放在对应文件里再为每个具体任务创建独立合同。一份可以直接复制的模板在项目中创建docs/ai-tasks/TASK-2026-0701.md写入下面的内容# Task Contract ## Goal 修复订单使用空优惠券时出现的金额计算错误。 ## Current behavior 当 coupon 为 null 时calculateDiscount() 抛出异常 接口最终返回 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 测试 - 不出现允许列表之外的文件变更 ## Required validation npm test -- tests/domain/order/discount.test.ts npm run lint npm run typecheck ## Diff budget - 最多修改 2 个文件 - 预计新增或修改不超过 80 行 ## Stop conditions 出现以下情况时停止修改并向开发者确认 - 必须修改允许列表之外的文件 - 现有实现与任务描述不一致 - 需要调整公共接口 - 测试失败原因与本次任务无关 - 无法在不改变业务规则的情况下完成这份合同最有价值的部分不是“Goal”而是允许文件、禁止改动和停止条件。大多数AI编码任务都会写目标却很少明确告诉智能体什么时候不能继续。先让AI交计划不要立即写代码合同准备好以后不要直接输入“开始修改”。先让AI完成一次只读分析阅读项目规则和 docs/ai-tasks/TASK-2026-0701.md。 当前阶段只做分析不修改任何文件不运行具有写入效果的命令。 请输出 1. 根因判断 2. 计划修改的文件 3. 每个文件需要修改的内容 4. 计划新增或调整的测试 5. 将要执行的验证命令 6. 是否存在超出合同范围的风险。 如果计划涉及 Allowed files 之外的文件立即停止并说明原因。这一步相当于在AI写代码之前做一次小型设计评审。如果它的计划里突然出现src/api/order-controller.ts package.json src/shared/money.ts就说明实际任务可能比预期更大或者AI正在扩大修改范围。此时应该调整合同、拆分任务或者让它换一种实现方案而不是等十几个文件被修改后再回滚。把长期规则和单次任务分开不少项目会把所有要求都塞进一份规则文件时间久了内容越来越长。其中既有长期有效的编码规范也有只适用于某次需求的临时要求。智能体每次工作都要读取这些内容容易出现冲突。更稳妥的分工是项目级规则用于保存长期约束# AI Development Rules - 修改代码前必须先输出计划 - 优先完成最小变更不主动重构无关代码 - 不得通过删除测试或降低校验标准让任务通过 - 修改后必须运行任务合同指定的验证命令 - 发现需求冲突时停止并询问 - 每次任务必须读取对应的 Task Contract任务级合同只描述当前需求docs/ai-tasks/TASK-2026-0701.md任务完成后这份合同还可以作为Review记录解释为什么只改了这些文件以及哪些风险经过了人工确认。Cursor官方推荐将项目规则放在.cursor/rules中并支持按文件路径应用不同规则简单项目也可以使用根目录下的AGENTS.md。规则应当具体、可执行并避免过于笼统。Cursor Rules官方文档Claude Code可以通过项目中的CLAUDE.md记录架构、编码标准和常用命令也支持在子目录中放置更具体的规则。Claude Code内存文档Diff Budget不是越小越好合同里设置了最多修改2个文件 预计新增或修改不超过80行这个数字不是硬性的代码质量标准而是异常提醒。有些任务确实需要修改更多文件。例如一个公共类型被多个模块引用合理修改可能超过最初估算。Diff Budget的作用是让智能体意识到“当前方案已经超出预计范围我应该先解释而不是继续扩散。”如果任务计划修改2个文件最终却出现18个文件开发者应该在Review之前就收到提醒。不要为了满足行数限制把本应清晰的代码压缩成难以维护的写法。边界控制的目标是减少无关修改而不是追求最少字符。修改完成后用Git做范围检查AI说“只修改了两个文件”不能代替实际检查。先查看工作区状态gitstatus--short它可以帮助发现已修改文件和新建但尚未跟踪的文件。再查看与当前提交相比发生变化的文件gitdiffHEAD --name-only检查修改规模gitdiffHEAD--stat检查空白错误和冲突标记gitdiff--check查看完整差异gitdiffHEAD本次示例的预期文件应该只有src/domain/order/discount.ts tests/domain/order/discount.test.ts如果出现其他文件不要立即接受“这是必要修改”的解释。让AI逐个说明列出 Allowed files 之外的所有变更。 对每个文件说明 - 为什么必须修改 - 不修改会导致什么问题 - 是否可以撤销 - 是否应该拆成独立任务。 不要继续编辑。能撤销的无关修改应当撤销确实必要的变化则更新合同并重新确认范围。测试通过也不代表任务完成一个越界修改可能完全通过测试。例如AI为了修复一个金额计算错误顺手改变了接口错误码。单元测试没有覆盖这个行为CI仍然是绿色但下游调用方可能受到影响。验收需要同时检查三件事功能是否符合预期原有行为是否保持修改范围是否符合合同。示例中的验证命令是npmtest-- tests/domain/order/discount.test.tsnpmrun lintnpmrun typecheck具体项目应该替换成真实可用的命令不要为了让模板看起来完整而编造测试脚本。如果任务依赖数据库、第三方API或私有环境还要在合同中写明无法本地验证的部分以及需要人工完成的检查。一套更稳的AI编码流程完成一次任务时可以把流程固定为明确问题 ↓ 编写变更合同 ↓ AI只读分析 ↓ 人工确认计划 ↓ AI执行最小修改 ↓ 运行测试与检查 ↓ Git核对修改范围 ↓ 人工Review这套方法适用于Cursor、Claude Code、ChatGPT辅助编码以及能够读取仓库并修改文件的其他AI工具。模型能力越强越需要清晰边界。因为它不仅能完成你要求的修改也更可能主动处理它认为应该一起解决的问题。如果你长期使用ChatGPT Plus、Claude Pro、Cursor、Kiro、Gemini Advanced或Grok也可以通过gpt108.com了解会员充值需求。它是第三方AI会员充值平台使用前应看清套餐说明、账号要求和售后规则。AI编码真正危险的状态不是它明确说“我不会”而是它交出一份看起来很完整、却混入大量无关变化的代码。下一次让AI修改项目之前先花两分钟写一份变更合同。这两分钟可能会省掉半小时的Diff检查和一次不必要的回滚。