约束衰减:AI 写的代码为什么经不起要求

今天 HN 上有一篇 450 分的 CMU 论文,标题很直接:Constraint Decay——结构约束对 LLM 代理在后端代码生成中表现的衰减影响。

这篇论文做了一个很系统、很干净的实验。

实验设计是这样的:用同一个 OpenAPI 规范——RealWorld Conduit API,19 个 CRUD 操作,5 个资源组(文章、评论、用户、个人主页、标签)。然后让 AI 从零开始生成后端服务。

它用了两个主流 agent 框架——Mini-SWE-Agent 和 OpenHands——配对六种不同能力层级的模型,从 24B 的小模型到 GPT-5.2。在八种不同的 web 框架上跑,包括 Python 的 Flask、FastAPI、Django、aiohttp 和 Node.js 的 Express、Fastify、Hono、Koa。

关键的设计是逐步加约束。

L0 是基线——只指定用哪个框架,没有别的约束。L1 加一个约束——比如用 Clean Architecture 或者用 SQLite 或者用 PostgreSQL。L2 加两个约束。L3 加三个约束全部加上——Clean Architecture + PostgreSQL + ORM。

总共 80 个生成任务。每个任务用 Docker 隔离执行,构建阶段让 agent 写代码,评估阶段用 Postman 的 32 个请求、291 个断言来测试行为——完全不依赖实现细节。

结果很干净,也很一致。

无论哪个模型、哪个 agent 框架,加约束都会让成功率下降。从 L0 到 L3,最好的配置——OpenHands 加上 MiniMax-M2.5——也掉了 17 个百分点。最惨的是 OpenHands 加上 Qwen3-Coder-Next,掉了 45 个百分点,相当于 62% 的 L0 成绩蒸发。

这不是"小模型不行、大模型可以"的故事。即使是 GPT-5.2,也掉了 30 个百分点。

更细致地看,约束之间的影响不均匀。Clean Architecture 本身只让断言通过率降了 9 个百分点。SQLite 降了 14 个百分点。PostgreSQL 降了 19 个百分点。ORM 几乎不降——SQLAlchemy 只降 1.5 个百分点,Sequelize 降 0.6 个百分点。

嗯,这说明了一个很具体的事情:AI 写代码的能力边界,不在架构模式,不在 ORM,在数据库。

这很好理解。Clean Architecture 是文件组织的问题,AI 很擅长复制模式。ORM 有大量的代码示例,AI 可以从训练中习得。但数据库不同——它要求 AI 不仅会写查询,还要理解连接、事务、schema 创建、环境配置、Docker 网络。PostgreSQL 比 SQLite 多了配置层:连接字符串、用户认证、数据库创建、健康检查。

论文里还有一个很有意思的发现:框架敏感度。Express 和 Koa 表现最好,平均 51% 的断言通过率。Django、FastAPI、Hono 垫底,只有 18%-25%。

这个结果和直觉不太一样。FastAPI 在 Python 生态中是最流行的异步框架之一,但在 AI 代码生成中表现很差。论文解释是因为 FastAPI 和 Django 需要更多项目设置——依赖注入、路由前缀处理、异步事件循环配置。这些"设置工作"对开发者来说是第二天的事情,但 AI 会在这些细节上浪费迭代和 token。

失败原因的分类也值得看。作者用了一个 LLM judge 来分类 222 个失败的 run。近 71% 是逻辑错误——服务器能启动,路由注册正确,但行为不对。这里面最大的一类是"不正确的查询逻辑"(25%),其次是"认证配置错误"(23%)和"DB/ORM 运行时错误"(21%)。

这些错误有一个共同特点:它们不是"不会写",而是"写了一点但不完全对"。join 写错了、filter 条件漏了、token 解析有 bug。AI 看起来在做正确的事,但细节不对。

论文里还有一个很重要的验证实验——"特征实现"。他们把 RealWorld 仓库里已有的功能代码删掉,让 AI 在已有的、完整的代码库里重新实现被删除的功能。这模拟了更接近真实开发场景的情况——不是从零开始,而是在现有代码库里加功能。

结果依然很弱。只有 GPT-5.2 超过了 50% 的 pass@1。这说明约束衰减不是"从零开始"的人为难题。即使在有现有代码库可以参考的情况下,AI 依然 struggle。

嗯,这篇论文让我想到前几天那篇 Thoughtworks 的研讨会报告。Thoughtworks 说严谨性在搬家——从代码审查搬到规格、测试、约束。这篇论文从另一个角度回答了同一个问题:你把严谨性放在哪里,AI 的表现就停在哪里。

把约束放在 agent 的 prompt 里?它会忽略。把约束放在 CLAUDE.md 里?它偶尔遵守。把约束编译进类型系统?这才是有效的。

这篇论文的约束是"软"的——写在 prompt 里的要求。没有编译时检查,没有类型系统强制。所以 AI 经常"看起来在遵守但实际上没做到"。

这也解释了为什么结构反压有效——Brooks 那篇文章里,Shen 生成的 Go 构造函数是不可伪造的。isMember 是 false,构造函数直接返回错误。这不是 prompt 里的"请记住",这是编译器的机械"不"。

对实际工作来说,这篇论文的价值不在于证明"AI 写不了生产代码"。它确实能写,而且写得不差。它的价值在于精确告诉你:AI 写的代码在哪些地方会出问题。

数据库层。跨文件的协调。配置和启动。认证。这些是边界条件,是"连接"的地方,不是"实现"的地方。AI 擅长实现,不擅长连接。

所以也许真正的工程变革不是让 AI 写更多代码。而是让 AI 写代码,人类负责设计那些"连接"的地方——架构、数据模型、安全策略。然后把这些连接写成机器可以检查的约束。

这样 AI 不会在边界处犯错。因为它碰都碰不到边界。