ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI生成代码为何改坏系统?防御与审查实战指南

AI生成代码为何改坏系统?防御与审查实战指南 为什么 AI 把功能做完了系统却被改坏了如果你最近在用 AI 辅助写代码一定碰到过这种诡异场景需求提过去AI 三分钟把功能写完了测试也过了你正想夸它效率高结果一上线老功能崩了。不是偶发是高频。我最近连续接手了几个项目都撞上同一种病功能是新的系统是坏的。排查到最后问题几乎都出在同一个地方——AI 生成的代码本身没错但它没有“融入”现有系统的能力。它不理解你的业务上下文、不熟悉你的代码规范、更不知道某个看似无关的模块其实跟它改的这段逻辑有隐含依赖。于是它自顾自地把新功能做完了留下的却是一堆隐性地雷。这篇文章我不讲大道理就用我实际排查过的几类事故把“AI 改坏系统”的典型路径、底层原因和能落地的规避方案一次性说透。1. 现象复盘AI 不是写错了是改“野”了1.1 三种最常见的事故场景先说结论AI 写代码很少出现语法错误或逻辑完全跑不通的情况真正危险的恰恰是它“看起来都对”的输出。我归纳了三种高频事故场景第一种新功能动了全局状态老功能被连带污染。典型场景是用户登录态、缓存、配置中心这类全局变量被 AI 生成的代码悄悄改掉。AI 不熟悉项目里谁在监听这个状态只知道自己需要设置一个值结果老接口的行为全变了。第二种AI 按照“通用最佳实践”重构了局部逻辑破坏了项目的隐性约定。比如项目里一直用某个自定义的日期工具类处理时间而 AI 在新增功能时直接用了现成的日期库。单看新代码优雅、简洁、没有任何问题。但项目里其他地方都在等那个工具类的特定返回值格式一混合数据对不上了。第三种AI 修改了公共函数或基类所有调用方一起遭殃。这个最致命。AI 接到“优化某个公共方法的性能”这类指令时经常直接把方法签名改了或返回值类型换了。它不会全局搜索谁在调用这个方法、调用方期待什么。你让 AI 改一个函数它实际上帮你改了一个 API。1.2 为什么“测试通过”却照样出事很多人会困惑AI 生成的代码不是有测试吗为什么测试过了还会崩因为 AI 生成的测试代码和 AI 生成的业务代码往往是“同一个错误世界观”下的产物。它给自己写验证用的是同一套假设。它认为user_id永远存在测试就构造有user_id的用例它认为缓存不会失效测试就不模拟缓存击穿。测试全绿恰恰说明测试和代码一起错了——这在 AI 辅助开发里是常态也是最迷惑人的地方。记住一句话AI 的测试证明的是“AI 自己理解的需求”被满足了而不是“你的业务需求”被满足了。1.3 事故现场的真实复盘我手头一个项目AI 负责给订单模块加一个“批量导出”功能。它完成了功能本身也正常。但上线半小时后有人反馈“订单详情页打开极慢”。查了半天原因是 AI 在批量导出时为了方便把订单列表接口的返回结构多包了一层data字段。它自己用的新结构写得对但老的前端页面还在解析旧结构——不是报错而是某种深度为 2 的循环里取不到字段不崩就是慢慢得像死循环。这不是 AI 蠢是它根本不知道有个老前端在消费这个接口。AI 没有“影响面分析”的能力它只有“实现当前需求”的能力。2. 技术内幕它为什么会“做完功能、改坏系统”2.1 AI 建模的是“代码语义”不是“系统架构”要理解这个问题得先说清楚大模型生成代码的底层逻辑。AI 写代码本质是基于海量代码样本学习到的“模式补全”。你给它一个 prompt它在你描述的约束下补全一段概率最高的 token 序列。这听起来没什么但后果很严重AI 的理解是基于“代码语义”的而不是基于“系统架构”的。它能理解“这段代码实现了什么逻辑”但它不理解“这个类在系统里的位置、它和哪些模块有隐式耦合、改动它会影响哪些下游”。用人话说AI 看到一棵树它描述得清清楚楚但它不知道这片森林里哪棵树倒了会砸到哪条路。2.2 上下文窗口是硬约束更是安全边界很多人以为是提示词写得不够好其实不是。就算你把整个项目文档都塞进上下文也有两个问题解决不了。第一代码库太大装不下。中大型项目的核心代码动辄几十万行任何模型的上下文窗口都装不下。你只能给它看“相关的”几个文件而“相关的”这个判断本身就是人做的AI 无法自己划定影响边界。第二隐式知识不在代码里。很多关键约定写在文档、口口相传、甚至某个同事的注释里。比如“这个接口不能加缓存因为下游要实时数据”这种约束你在代码里根本找不到AI 自然也学不到。所以 AI 能做到的最好情况是在“局部正确”的同时对“全局无知”。2.3 单点正确与全局错误的经典矛盾我用一个特别简单的例子解释这个矛盾假设系统里有一个formatMoney()函数全项目所有金额展示都靠它。某个新需求要求“部分页面显示原始金额”AI 接到任务后在某个新页面里直接写amount.toString()。单看这个新页面完全正确。但系统管理员做数据对账时发现新老页面金额格式不一致以为是系统 bug脏数据进库了整个对账流程瘫痪。AI 的产品经理不会说 AI 错了它确实做到了“显示原始金额”。但对系统来说它破坏了金额展示的一致性。这就是核心AI 的“正确”是单点正确系统的“正确”是全局一致。二者冲突时系统必然受损。2.4 别忽略这层AI 在“顺从”你不是在“挑战”你还有一层心理因素也很关键。AI 是服务型工具它的训练目标里包含了“让用户满意”。你让它“加一个批量删除功能”它绝不会反问“你确定要批量删除吗这个操作不可恢复是否需要二次确认”它会把功能给你做出来而且做得特别完整、特别顺畅。至于这种操作是否应该加防呆设计、是否需要权限校验、是否需要操作审计——这不是它能替你决策的。这种“顺从性”在功能开发时是好东西但在涉及系统稳定性时它可能是毒药。AI 不会主动保护你的系统它默认你什么都知道。3. 实战避坑五条可以直接落地的防御手段既然 AI 一定会“改野”我们能做的就是在流程上把它圈住。以下五条是我在实际项目中验证过有效的手段全部可以直接落地不需要什么高深工具。3.1 给 AI 划“施工区”明确告知禁止改动范围这是最便宜也最有效的手段。每次让 AI 改代码prompt 里明确列一条“本次任务只允许修改src/order/export.ts文件禁止修改任何其他文件尤其是src/utils/money.ts、src/api/order.ts和全局状态相关文件。”我试过很多次加了这条之后AI 越界改动公共文件的概率至少下降一半。原理不复杂AI 在生成时会把约束当成强条件去满足你给了明确的物理边界它就不会“自由发挥”到隔壁模块。3.2 强制“先计划后编码”的工作流不要一上来就丢需求让 AI 写代码。先让它输出一份“改造计划”包含涉及的文件列表每个文件要改什么影响到的调用方是否涉及公共函数/接口签名变更你像评审同事的方案一样评审这份计划确认无误后再让它按计划实施。这样做有两个好处。第一AI 被迫在动手前先做影响面分析即使做得不完整也比直接开写强。第二你有机会在它动手前发现“它会碰公共函数”这类危险动作。3.3 用 Git Diff 做“手术级”审查而不是“浏览级”很多人审查 AI 代码的方式是打开 diff 一眼扫过去看着数量不多代码也不复杂就合了。这不对。AI 代码审查要像外科手术一样每一行改动都得回答三个问题——为什么要改会不会影响别的地方有没有更保守的替代方案尤其是以下三类改动必须重点盯防改动公共工具函数、基类、全局状态改变函数签名或返回值结构新增依赖库AI 很喜欢引入新依赖哪怕只是少写两行代码3.4 回归测试前置任何 AI 改动都跑全量我在团队里立了一个铁规矩AI 生成的代码合并前必须跑一次全量测试而不是只跑它自己生成的那几条用例。理由前面说了AI 的测试和 AI 的代码是“同源”的一起错的概率极高。全量测试至少能覆盖到老功能能拦下一部分回归问题。如果你项目没有全量测试那就退而求其次把老功能的核心路径列成检查清单让 AI 生成的代码在这几条路径上手工跑一遍。3.5 加一层“人工语义审查”别迷信 AI 自查让 AI 自己检查自己的代码效果极其有限。它检查的是“是否实现了需求”而不是“是否破坏了系统”。我试过让 AI 自查“改动是否可能影响其他模块”它的回复永远是“本次改动仅涉及 XX不影响其他模块”——哪怕它实际上改了公共函数。所以最终一道审查必须是人来做。不是让你逐行读代码而是让你回答一个问题这个改动站在系统架构的角度合理吗4. 纵深防御从提示词到自动化门禁的完整体系上面五条是单点防御各有作用但如果你在一个稍微正规一点的团队里需要把它们串成一个体系。我介绍一下目前在用的组合方案。4.1 提示词模板一条可以直接抄的注入式防御你是一名资深后端工程师。接下里的任务需要修改现有系统代码。 【硬性约束】 1. 只允许修改以下文件{file_list} 2. 禁止修改任何公共函数、工具类、全局变量、缓存配置、数据库 schema 3. 禁止引入新的第三方依赖 4. 如确需修改公共函数必须停下来先输出影响面分析等待人工确认 【工作流程】 1. 先输出本次改造的完整计划涉及文件、改动点、影响范围 2. 等待确认后再开始写代码 3. 写完代码后列出“可能受影响的既有功能点”供测试参考 【需求】 {task_description}这个模板不是万能的但能有效降低 AI 的“发挥空间”。核心思路是把“系统保护”从隐性要求变成显性约束。4.2 工程化防线Diff 检查脚本与 CI 门禁如果你对 AI 生成的代码不信任可以在工程层加一道自动化防线。一个简单的思路是写一个 Git 检查脚本专门盯防 AI 改动高危文件。#!/usr/bin/env python3 AI 代码改动安全检查脚本 用法: python3 check_ai_diff.py base_branch target_branch import subprocess import sys # 高危文件清单: 公共函数、全局状态、配置文件等 HIGH_RISK_FILES [ src/utils/, src/core/, src/config/, src/store/global_state.py, ] # 新增依赖检测 DEPENDENCY_FILES [ requirements.txt, package.json, go.mod, pom.xml, ] def get_diff_files(base: str, target: str) - list: result subprocess.run( [git, diff, --name-only, base, target], capture_outputTrue, textTrue, ) return result.stdout.strip().split(\n) def check_high_risk_changes(files: list) - list: violations [] for f in files: for risk in HIGH_RISK_FILES: if f.startswith(risk): violations.append(f) return violations def check_dependency_changes(files: list) - list: violations [] for f in files: if any(dep in f for dep in DEPENDENCY_FILES): violations.append(f) return violations def main(): if len(sys.argv) ! 3: print(Usage: python3 check_ai_diff.py base target) sys.exit(1) base, target sys.argv[1], sys.argv[2] files get_diff_files(base, target) high_risk check_high_risk_changes(files) deps check_dependency_changes(files) if high_risk: print(f高危文件被改动: {high_risk}) sys.exit(1) if deps: print(f依赖文件被改动需要人工确认: {deps}) sys.exit(1) print(安全检查通过可以继续审查。) if __name__ __main__: main()这个脚本可以挂在 CI 里也可以在本地跑。它的目标是拦截“AI 越界改文件”的情况而不是替代人工审查。实测下来至少能把最危险的几类问题拦在合并前。4.3 团队约定AI 代码的“隔离区”策略再分享一个更管理向的手段。如果团队里 AI 使用频率很高可以约定 AI 生成的代码一律放在独立目录或独立分支里由人工完成“注入”动作。什么意思就是 AI 只在feature/ai-generated/xxx这类分支里工作它生成的代码只新增文件不修改任何现有文件。真正需要修改现有代码时人来做——或者把现有代码先复制一份到 AI 目录里改再由人负责合并回去。这个策略牺牲了一部分效率但换来了极高的安全性。AI 永远没有机会直接“碰”生产代码它的所有输出都要经过一次人工搬运。这个“搬运”的过程其实就是一次强制代码审查。5. 常见误区和排查技巧5.1 误区一以为是提示词写得不够好很多人遇到 AI 改坏系统第一反应是“我 prompt 没写清楚”。这种归因有道理但不完整。提示词写得再清楚AI 也无法理解“系统级影响”这类抽象概念。你可以把需求描述得极其精确但它依然不会去全局搜索调用方不会去分析接口变更的连锁反应。这是模型能力的边界不是提示词技巧能突破的。排查建议与其纠结提示词不如检查自己的“防御机制”是否就位。有没有限制改动范围有没有做影响面分析有没有跑全量测试5.2 误区二出了问题就全线禁止 AI另一种极端是“一朝被蛇咬十年怕井绳”出一次事故就禁止团队用 AI。这同样不可取。AI 的价值是实实在在的尤其是生成独立功能、写测试用例、解释陌生代码这些场景效率提升极其明显。关键在于把它放在合适的流程位置里——它适合做“实现者”不适合做“系统架构师”。排查建议建立 AI 使用的“分级授权”制度低风险任务新文件、独立函数可以直接用 AI中风险任务修改现有模块需要人工审查高风险任务改公共函数、数据库结构AI 仅提供方案不直接执行。5.3 排障手册真改坏了怎么快速定位如果 AI 已经把系统改坏了下面这套排查顺序是我实践过最高效的看 Git 历史找到 AI 最近的提交记录确认它改了哪些文件。重点看有没有utils、core、config这类目录下的改动。查公共函数变更如果 AI 改了某个公共函数用git log -p function_name或 IDE 的 Find Usages 功能把所有调用方列出来逐一检查。跑旧接口回归不要只测新功能。把老接口的核心路径用 Postman 或 curl 跑一遍重点看返回结构、状态码、耗时。查依赖变更如果 AI 引入了新依赖检查版本冲突。AI 经常无视项目里已有的库自己装一个新的导致多个版本并存行为异常。回滚与重做如果定位到就是 AI 的锅最快的方案是git revert它的提交然后用手动方式重新实现这个需求——不是重新让 AI 写而是你自己写或者至少你自己写好边界再让 AI 填空。5.4 一个高效的“AI 代码验收”清单最后给一份可以直接用的验收清单。每次 AI 写完代码你逐项打勾全部通过再合入检查项具体问题通过标准改动范围AI 是否只改了你指定的文件是公共代码是否动了公共函数、工具类、全局状态否如动了必须有人工说明依赖变更是否新增了依赖库否如需要必须有人工确认数据结构是否改了接口返回结构或数据库 schema否如改了必须同步所有调用方回归验证老功能的核心路径是否全部跑通全部通过测试有效性测试用例是否覆盖了老功能的回归场景是人工审查是否有人从系统架构角度审查过已确认6. 写在最后AI 是得力的施工队你得当好项目经理我在实际使用中体会最深的一点是AI 不是一个“系统开发者”它是一个“功能实现者”。你把一堵墙的施工图画好、材料备好、边界划定它能干得又快又好但你如果只丢一句“把这里改一下”它很可能拆掉承重墙去帮你做个飘窗。这不是 AI 的缺陷是它的本质。就像你不能怪一个实习生不了解公司政治一样你不能怪 AI 不理解你的系统架构——它从未拥有过“理解系统”的机会。所以真正要改变的是我们的工作方式。把 AI 当施工队自己当好项目经理。控制范围、注重检查、把握关键节点让 AI 在划定的边界内发挥效率而不是指望它替你做全局决策。最后再分享一个小技巧。如果你发现某个 AI 反复改坏同一类东西不妨把这条“教训”写成一个永久性的约束加到团队的 AI 提示词模板里。比如我们团队现在模板里固定有一条“本项目所有金额计算必须使用Decimal禁止使用浮点数。” 这看着跟 AI 改代码没关系但 AI 一旦触碰金额逻辑就会自动避开浮点这个雷区。管好了边界AI 就是最好的效率工具管不好边界它就是系统稳定性最大的变量。希望这篇文章能帮你管住那条边界。
返回列表