给 Codex 戴上紧箍, 治一治 AI 的过度发挥

最近用 Codex 写代码的时候,我逐渐发现一个问题:

AI 不是不会写代码。

它的问题是,太会写了。

你让它改一个接口字段,它开始写兼容逻辑:

ts 复制代码
const title = data.title || data.name || data.label || data.displayName || ''

你让它修一个按钮文案,它顺手重构了半个组件。

你让它补一个 loading 状态,它顺便抽了 hook、拆了 utils、加了类型、优化了结构,最后还很贴心地告诉你:"这样更具可维护性。"

这种感觉很熟悉。

就像你请朋友来家里帮忙换个灯泡,结果他看了看天花板,说:"你这个房子的采光系统有点问题,我给你重新设计一下电路。"

谢谢,但我只是想让灯亮。

于是,我开始给 Codex 准备一套开发规则,也就是 codex-dev-norms 这类 skill。它的目标不是让 AI 变笨,而是让 AI 在写代码之前先明白一件事:

不是所有能写的代码,都应该写。

一、背景:AI 写代码的常见"热心事故"

AI 开发里有一个非常典型的问题:它经常会"补过头"。

人类工程师写代码时,多少会带着项目上下文、团队约定、业务边界,以及一点点来自线上事故的心理阴影。

AI 不一样。

它擅长从已有模式中推断下一步,也擅长把"不确定"补成"看起来合理"。这在很多场景里很有用,但在业务代码里,它也可能制造一些非常熟悉的事故现场。

1. 过度兼容

比如接口文档里明明写的是 title,AI 可能觉得后端也许会返回 name,历史数据也许会有 label,保险一点再兼容 displayName

于是代码变成:

ts 复制代码
const title = item.title || item.name || item.label || item.displayName

看起来很稳。

实际上很危险。

因为它把一个明确的接口契约,变成了一场字段猜谜游戏。今天兼容四个字段,明天再来一个 text,后天补一个 caption,最后谁也不知道真实数据到底应该长什么样。

代码开始像在相亲:谁来都能接一下。

2. 过度重构

你只想让它修一个 bug,它却觉得附近代码不够优雅。

"这个函数可以抽一下。"

"这个组件可以拆一下。"

"这个命名可以统一一下。"

"这个结构可以更通用一点。"

如果是在学习项目里,这可能还挺开心。但在真实业务项目里,这种"顺手优化"经常会变成一次范围失控。

一个小改动突然牵扯多个文件,原本只需要验证一个按钮,现在要确认半个页面有没有回归。

AI 倒是很积极,Code Review 的人开始沉默。

3. 过度抽象

AI 看到重复代码,很容易触发封装欲。

两个地方都有一个状态判断?抽。

两个组件 class 有点像?抽。

两个类型字段差不多?合。

但工程里的重复并不总是坏事。有些重复只是长得像,业务含义并不一样;有些代码现在重复,未来变化方向可能完全不同。

强行抽象之后,项目可能从"有一点重复"升级成"没人敢动"。

4. 忽略编码和中文环境

中文项目还有一个经典问题:编码。

尤其在 Windows 环境里,如果读写文件时不确认 UTF-8、GBK、ANSI,轻则中文变乱码,重则你打开文件那一刻,仿佛看见项目在用另一种文明和你交流。

还有些 AI 会把中文写成一串 Unicode 转义字符。

这当然也许能运行。

但人类读起来会很想下班。

5. 缺少"改动边界感"

说到底,很多问题不是 AI 不懂代码,而是不知道边界。

它不知道哪些文件不能动。

它不知道哪些字段不能猜。

它不知道哪些重复可以保留。

它不知道这个项目现在需要的是"修复",不是"翻新"。

于是我们需要一套规则,让 AI 在动手前先收到明确提醒:

做需要做的事,不要做看起来也不错的事。

二、有哪些解决办法

要解决 AI 过度发挥,常见办法有几种。

1. 靠人工 Code Review

这是最传统也最可靠的方式。

AI 写完,人类审。

问题是,AI 写得太快了。

它一分钟可以生成一大片代码,而人类要逐行确认:这个兼容有没有必要、这个抽象有没有收益、这个字段是不是瞎猜、这个文件是不是不该动。

久而久之,Code Review 会从"质量保障"变成"AI 善后现场"。

2. 靠 Prompt 反复提醒

比如每次都告诉它:

text 复制代码
只做最小改动。
不要重构。
不要猜接口字段。
不要修改无关文件。

这当然有用。

但问题是,每次都要说。你忘说一次,AI 就可能立刻恢复出厂设置,重新变成那个看到代码就想顺手优化的热心同事。

3. 靠项目级规则文件

比如 AGENTS.mdCLAUDE.md 或者 Codex skill。

这种方式的好处是:把团队约定固化下来。

不是每次靠人提醒,而是让 Codex 在工作时就能读到项目边界。哪些事可以做,哪些事不能做;什么时候需要查接口,什么时候必须保持最小改动;什么时候该加测试,什么时候不要为了覆盖率写废测试。

它相当于给 AI 配了一份"入职手册"。

只不过这份手册的重点不是欢迎加入团队,而是:

欢迎加入团队,先别乱动。

4. 靠技能化拆分规则

更进一步,可以把规则拆成多个 reference。

比如 API 有 API 的规则,前端有前端的规则,测试有测试的规则,Git 工作区有 Git 工作区的规则。

这样 Codex 不需要每次背完整本规章制度,而是根据任务读取相关部分。

修接口,就看 API 规则。

改组件,就看前端规则。

做 review,就看 review 规则。

这种方式比较接近真实工程协作:不是把 AI 当成一个万能文本生成器,而是把它当成一个需要遵守项目规范的开发者。

三、这个 skill 具体解决了哪些问题

codex-dev-norms 这套 skill 的核心价值,是把 AI 常见的失控点拆成一个个明确边界。

它不是一份"代码洁癖清单",更像是一份"防止 AI 过度热心说明书"。

下面按 reference 大致看一下它管了哪些事。

1. code-change-boundary:先管住手,别乱改

这个 reference 主要强调最小改动原则。

也就是:任务要求什么,就改什么;不要因为"更干净""更通用""更优雅""更符合最佳实践"扩大范围。

这条非常关键。

因为 AI 很容易把"解决问题"理解成"顺便把附近看不顺眼的地方都整理一下"。但真实项目里,很多时候我们需要的是可控的小变更,而不是一次兴致勃勃的局部装修。

这条规则会提醒 Codex:

你是来修这个 bug 的,不是来给项目重新投胎的。

2. global-frontend:中文、编码、前端全局边界

这个 reference 管的是全局前端开发规则,尤其是编码和中文字符处理。

它要求在读写文件前确认编码,不要默认使用可能出问题的读取方式;中文要直接写中文,不要写 Unicode escape;如果发现乱码或混合编码,要先处理编码问题。

这对中文项目特别重要。

毕竟中文乱码不是普通 bug,它更像一种仪式。一旦触发,项目里所有汉字都会开始怀疑自己到底来自哪里。

除了编码,它也强调不要改无关代码、不要格式化无关内容、不要乱改结构、命名、props、CSS class 等。

一句话:前端可以改,但不能趁机"大扫除"。

3. api-integration:别猜接口,按定义来

这个 reference 专门管 API 接入。

它的核心态度很明确:API 字段必须严格来自真实定义。

不要猜字段名。

不要猜响应结构。

不要加别名字段。

不要写 a || b || c 这种自动兼容。

如果字段不清楚,就去确认接口定义。

这正好命中 AI 的常见毛病:太喜欢替后端"考虑周全"。

但业务代码里,接口契约不是开放式作文。后端返回什么,前端就按什么接。否则所谓兼容会慢慢变成技术债,最后谁也不知道哪个字段才是真的。

4. backend-business-logic:业务逻辑别到处流浪

这个 reference 关注后端和业务逻辑边界。

它强调业务规则应该待在真正属于它的位置,不要散落在 controller、service、job、mapper、serializer 各处。

这也是 AI 容易犯的错。

哪里需要判断,就在哪里补一段。

哪里缺个转换,就在哪里加一个 helper。

短期看问题解决了,长期看业务规则开始到处流浪。以后要改一个状态判断,可能要在五个地方找它的分身。

这条规则是在提醒 AI:业务逻辑不是贴纸,不能哪里空就贴哪里。

5. frontend-implementation:小组件别写成小宇宙

这个 reference 关注前端实现。

它强调可读性优先,不要为了优化引入复杂度;组件应该小而聚焦;是否抽 hook、utils,要看真实复杂度,而不是主观觉得"这样更优雅"。

这对 AI 很有必要。

因为 AI 很擅长把简单事情做得很完整。

一个本来只在当前组件里用一次的逻辑,它可能会抽成 hook;一个简单 formatter,它可能会塞进 utils;一个局部状态,它可能会设计成可复用方案。

看起来很专业。

但专业不等于复杂。

尤其前端代码,很多时候"放在这里一眼看懂",比"抽出去但你要点进去看"更好。

6. state-management-boundary:状态该归谁,就归谁

这个 reference 管状态边界。

它区分了组件状态、hook 状态、store 状态、URL query 状态和 server state。

这能解决一个很常见的问题:AI 喜欢把状态放到"看起来更通用"的地方。

本来只属于一个组件的展开状态,被放进 store。

本来应该体现在 URL 上的筛选条件,被藏在内部 state。

本来属于服务端缓存的数据,被复制成多份本地状态。

最后状态之间互相看不顺眼,页面刷新、返回、切 tab 都可能出现奇怪行为。

这条规则就是告诉 Codex:状态不是越全局越高级,放对地方才高级。

7. side-effects-and-subscriptions:副作用要有生命周期

这个 reference 管事件监听、订阅、定时器、watcher 等副作用。

它强调注册要清楚,清理要明确,生命周期要看得见。

AI 写副作用代码时,很容易只关心"让它生效",忘了"让它结束"。

比如加了 resize listener,但没有清理。

比如开了 timer,但卸载时不处理。

比如 watcher 和事件订阅重复触发同一段逻辑。

这种问题短期不一定明显,但跑久了就会变成难查的行为异常。

这条规则相当于提醒 AI:不是所有副作用都能靠缘分收场。

8. constants-and-magic-values:别让魔法值满地跑

这个 reference 管常量和魔法值。

比如业务状态、事件名、缓存 key、storage key、路由、API path、正则、默认值、分页数等。

这些值如果只有局部意义,可以留在局部。但如果代表共享业务含义,就应该有稳定来源。

AI 有时会随手写字符串:

ts 复制代码
status === 'success'

单看没问题。

但如果项目里到处都是 'success''SUCCESS''done''finished',以后改业务状态时就会很热闹。

这条规则的意义是:有业务含义的值,不要让它在项目里自由奔跑。

9. type-convergence:类型相同,不代表概念相同

这个 reference 管类型收敛。

它会提醒 Codex:如果多个类型字段相同,并且业务含义、生命周期也相同,可以考虑统一;但如果只是结构长得像,不要强行合并。

这很重要。

因为 AI 很容易看到两个 interface 字段一样,就觉得可以抽一个公共类型。

但前端表单类型、接口响应类型、数据库实体类型,可能字段一模一样,语义却完全不同。

强行合并之后,一个地方变动,另一个地方被迫跟着变,最后类型系统从保护伞变成连坐制度。

10. convergence-and-abstraction:不是所有重复都该抽象

这个 reference 管代码收敛和抽象边界。

它承认一个现实:允许合理的局部重复。

如果逻辑只出现一次,或者业务含义不同,或者未来变化方向不同,或者抽象会让代码更难懂,那就不该为了"消灭重复"而抽象。

这条规则非常反 AI 本能。

AI 看到重复,常常想收敛;看到相似,常常想封装。

但抽象不是奖励机制,不是写得越多越高级。好的抽象应该降低理解成本,而不是把简单代码变成参数迷宫。

11. ui-style-convergence:UI 风格可以统一,但别硬统一

这个 reference 管 UI 样式和变体收敛。

比如相同按钮、输入框、卡片、徽标、表格样式,重复的 class list,暗色模式规则,响应式布局规则,都可以考虑收敛到基础组件、设计 token 或 variant。

但它不是鼓励 AI 看到 class 重复就马上抽组件。

UI 里有些重复来自设计系统,有些重复来自局部业务场景。前者值得收敛,后者未必。

所以这条规则的价值是:让 AI 既能识别真正的设计重复,也别把每个长得像的东西都拉去认亲。

12. engineering-config-convergence:工程配置别各玩各的

这个 reference 管工程配置收敛。

比如 TypeScript 配置、lint 配置、测试配置、构建配置、formatter、package scripts、依赖版本、环境变量 schema、发布流程等。

这些地方一旦重复且不一致,就很容易出现"我这里能跑,你那里不行"的经典剧情。

但同样,它也不是让 AI 随便改配置。

工程配置影响范围通常很大,所以只有当任务确实涉及配置收敛时,才应该动它。

换句话说:配置文件不是许愿池,不能路过就扔一个优化进去。

13. dependency-management:不要为小事加新包

这个 reference 管依赖管理。

它强调不要为了简单功能引入新依赖,不要引入职责重复的第二个库,不要在窄任务里升级无关依赖,也不要随便改 lockfile。

AI 有时会很喜欢推荐库。

格式化时间?加个库。

深拷贝?加个库。

简单校验?加个库。

但依赖不是免费的。它带来体积、维护、安全、升级和团队认知成本。

这条规则是在提醒 Codex:能写三行解决的问题,不要先召唤整个 npm。

14. docs-and-comments:注释解释为什么,不解释废话

这个 reference 管文档和注释。

它鼓励解释业务目的、分支原因、边界场景、状态流转、兼容约束、性能取舍和临时方案移除条件。

但它反对显而易见的注释,也反对没有 owner 或移除条件的 TODO。

AI 特别容易写这种注释:

ts 复制代码
// 设置标题
setTitle(title)

这不是注释,这是把代码又朗读了一遍。

好的注释应该告诉人类:为什么这里要这么做,而不是告诉人类:这行代码长什么样。

15. testing:测试行为,不测试自我感动

这个 reference 管测试。

它强调不要给薄包装写无意义测试,不要重复相同输入输出的测试,不要测试 mock 本身,不要为了覆盖率数字写测试。

有价值的测试应该覆盖正常路径、边界值、空值、非法值、权限、状态转换、错误处理、回归场景等。

这点也很适合约束 AI。

AI 可以很快写出一堆测试,但数量不代表质量。如果测试只是证明 mock 会返回 mock,那它更像是在给自己鼓掌。

16. error-handling-and-logging:错误不要被悄悄吞掉

这个 reference 管错误处理和日志。

它强调不要无声吞错,能明确失败就不要隐藏错误状态;处理错误要在真正能恢复的边界;转换或重新抛出错误时要保留有用上下文。

日志也一样。

要记录能帮助诊断真实问题的信息,不要给正常流程加一堆噪音,也不要把临时 debug 留进生产代码。

这条规则的目的很朴素:出问题时,人类至少要知道问题在哪里,而不是只能看着控制台思考人生。

17. naming-and-files:命名稳定,文件别乱拆

这个 reference 管命名和文件组织。

它强调一个概念应该有一个稳定名字,不同名字应该意味着不同含义;优先使用领域术语,不要随手起 helpermanagercommonbaseutil 这种模糊名字。

它也提醒不要为了文件变小就拆文件,不要为了方便把无关职责塞进一个文件。

AI 很容易"整理结构",但结构整理本身就是改动。

如果任务不要求,就不要顺手改命名、挪文件、拆模块。否则看起来只是组织调整,实际上可能影响导入路径、公共 API 和团队认知。

18. code-review:Review 先找风险,不先夸优雅

这个 reference 管代码审查。

它要求 review 优先关注错误行为、回归、边界条件、契约不匹配、缺失验证、不安全副作用、测试缺失等。

这能避免 AI review 变成空泛评价:

"整体结构清晰。"

"代码可读性较好。"

"建议进一步优化。"

这些话不是完全没用,但如果没有指出真实风险,就像开会时说"我们要持续改进"一样,听起来正确,落地困难。

好的 review 应该先把可能出事故的地方指出来。

19. final-response:交付时说人话

这个 reference 管最终回复。

它要求 Codex 在完成任务后说清楚:改了什么,验证了什么,还有什么不确定,下一步是什么。

这也很重要。

因为 AI 很容易把最终回复写成过程汇报:

"我首先分析了项目结构,然后检查了相关文件,接着进行了修改......"

用户真正关心的是结果。

所以这条规则会要求它开门见山:事情是否完成,完成到什么程度,有没有验证,哪里还需要注意。

四、它真正解决的不是代码问题,而是边界问题

看完这些 reference,会发现 codex-dev-norms 真正解决的不是某一种具体 bug。

它解决的是 AI 开发里的边界问题。

AI 很强,但它不知道你的项目里哪些东西不能碰。

它不知道这个接口字段必须严格按定义来。

它不知道这个重复现在不该抽象。

它不知道这个状态只属于当前组件。

它不知道这个中文文件不能随便用默认编码读写。

它也不知道你今天只是想修一个按钮,而不是顺便参加一次架构改造。

所以这套 skill 的意义,就是把这些隐含经验变成显式规则。

让 Codex 从"能写代码",变成"能按项目边界写代码"。

五、让 AI 少一点自我发挥,多一点工程克制

AI 写代码越来越强,这是事实。

但在真实项目里,强不等于可以随便发挥。

很多时候,我们需要的不是一个永远有新想法的 AI,而是一个知道什么时候该停手的 AI。

codex-dev-norms 这类 skill,就是给 Codex 准备的一套开发家规。

它不会让 AI 失去能力。

恰恰相反,它让 AI 的能力更可控、更稳定、更适合进入真实工程协作。

以前 Codex 像一个精力旺盛的搭子:你说一句,它能写一屏。

现在有了这套规则,它还是能写一屏。

但终于知道,哪半屏不该写。

相关推荐
sugar__salt1 小时前
从零落地 Generative AI 接口调用:Node.js 工程化最佳实践
人工智能·node.js
weixin_468466851 小时前
空洞卷积与膨胀卷积新手入门指南
图像处理·人工智能·深度学习·ai·机器视觉·卷积·空洞卷积
AI创界者1 小时前
ComfyUI v8 极致整合包发布!Win/Mac 双平台完美适配 + 多卡并行加速,开启 AI 绘画新时代
人工智能·macos
zhangfeng11331 小时前
本账号 自媒体 csdn 账号诊断和改进建议,记录一下
人工智能·机器学习·媒体
镭封1 小时前
影视解说、小说推文、情感语录,分别适合什么AI声音?
人工智能
ZPC82101 小时前
前馈补偿原理 + 分类 + 公式 + 工程实现(配合 PID 使用,从根源减轻闭环收敛压力)
人工智能·分布式·机器人
sensen_kiss1 小时前
CPT306 Principles of Computer Games Design 电脑游戏设计原理 Pt.8 Game AI(游戏里的“人工智能系统”)
人工智能·游戏
weixin_468466851 小时前
ResNet 残差网络新手入门与实战指南
人工智能·深度学习·ai·残差网络·resnet·机器视觉
jiayong231 小时前
harness 与 hermes-agent 扩展性、安全与运维
运维·人工智能·安全·ai·架构·智能体·harness