这两年看 AI 写代码,很容易产生一种错觉:只要模型足够强,后端开发迟早也能被一把梭。

我现在越来越不信这件事。

不是因为 LLM Agent 不会写代码。恰恰相反,它们已经很会写了。写接口、补 CRUD、生成迁移脚本、加测试、改几个文件,对今天的 coding agent 来说都不算稀奇。

真正麻烦的是另一件事:后端系统里最重要的东西,很多并不写在需求的第一行。

比如:

  • 这个接口能不能被普通用户调用?
  • 这个字段到底由谁维护?
  • 这次修改会不会破坏历史数据?
  • ORM 查询有没有绕过租户隔离?
  • 这个业务流程中间失败了,状态要怎么回滚?
  • 老代码里那些看起来别扭的写法,是历史包袱,还是故意留下的保护栏?

这些东西不像“写一个登录接口”那么显眼,但它们决定了后端代码能不能上线。

今天 Hacker News 上有篇论文被讨论得挺热,叫 Constraint Decay: The Fragility of LLM Agents in Backend Code Generation。它戳中了一个很关键的问题:LLM Agent 在后端代码生成里,往往不是一开始就离谱,而是在任务推进过程中逐渐忘掉约束。

我觉得这个词很准确:Constraint Decay,约束衰减。

说白了,就是 Agent 起步的时候还记得“要用这个架构、这个 ORM、这个数据库模式、这个 API 契约”,写着写着就开始自己找捷径。功能表面上可能跑通了,结构却慢慢歪了。

这才是后端 AI 编程最容易翻车的地方。

会写代码,不等于守得住系统边界

很多人评估 AI 写代码,第一反应是看它能不能跑。

这当然重要,但后端系统只看“能不能跑”太粗糙了。

一个接口返回 200,不代表它是对的。它可能绕过了权限校验,可能写错了租户 ID,可能把本该异步处理的任务塞进同步请求里,可能在高并发下把库存扣成负数。

这些问题在 demo 里不一定暴露。

更烦的是,LLM Agent 很擅长给你一种“看起来完成了”的感觉。它会补齐文件,会解释思路,会给出测试结果。你扫一眼,觉得大差不差。

后端最怕的就是这种大差不差。

前端页面错了,用户会立刻看到按钮不对、布局崩了。后端错了,可能要等到数据脏了、权限穿了、账算错了,你才知道它刚才那次“完成任务”其实埋了雷。

这也是为什么我不太喜欢“AI 已经能替代程序员”这种说法。它太粗暴了。AI 可以替代很多局部动作,但后端开发的核心从来不只是动作,而是边界感。

边界感是什么?

知道哪里不能乱动。知道哪些约束必须保留。知道一个短期能跑的改法,会不会把长期维护成本炸上天。

Constraint Decay 到底是什么意思

这篇论文做的事情挺直接:研究 LLM Agent 在多文件后端生成任务中,能不能同时满足功能需求和结构约束。

作者固定了一套 API contract,然后设计了 80 个 greenfield 生成任务和 20 个功能实现任务,覆盖 8 个 Web 框架。评估不只看端到端行为测试,还用静态 verifier 检查结构约束。

结果很扎心:当结构要求越积越多,Agent 的表现会明显下降。论文摘要里提到,较强的配置从 baseline 到 fully specified tasks,assertion pass rate 平均掉了 30 个点;一些较弱配置接近归零。

这不是小毛病。

它说明一件事:Agent 对“功能目标”比较敏感,对“结构约束”不够敏感。你让它实现一个接口,它会努力把接口跑起来;你让它同时遵守架构、ORM、数据库约束、框架惯例、项目风格、隐含边界,它就容易漏。

这很像让一个新人接手后端项目。

新人不是不会写代码,而是不知道哪里有坑。他看到一个查询慢,就手写 SQL;看到一个对象不好传,就直接绕过 service;看到权限判断麻烦,就先复制一段。单独看每一步都能解释,合起来就变成系统结构变形。

LLM Agent 更像一个速度特别快、记忆特别短、态度特别自信的新人。

这话有点刻薄,但我觉得很贴切。

后端的难点,是一堆看不见的约束叠在一起

后端代码难,不是因为语法复杂。

大部分后端代码的语法都挺朴素。真正复杂的是约束叠加。

1. 权限约束

一个接口到底给谁用?管理员、组织成员、项目 owner、匿名用户,权限边界完全不同。

Agent 很容易写出“功能正确,但权限太松”的代码。

比如用户传一个 project_id,Agent 直接查项目,然后返回数据。看起来很正常。但真正的系统里,还要检查这个用户是不是属于该组织,是不是有项目权限,是不是能看到这个字段。

少一个判断,demo 不一定挂,生产环境可能就穿了。

2. 状态约束

后端系统里,很多对象都有状态机。

订单从 pending 到 paid 到 shipped 到 completed。任务从 queued 到 running 到 failed 到 retried。账号从 active 到 suspended 到 deleted。

Agent 写代码时,很容易只看到“把状态改成 X”,却忘了状态能不能这么跳。

这类错误特别阴。测试数据少的时候,一切都正常;真实业务跑久了,就会出现幽灵状态。

3. 数据一致性约束

数据库不是一个大号 JSON 文件。

它有事务、唯一索引、外键、幂等、并发冲突、历史数据兼容。后端代码要对这些东西有敬畏感。

LLM Agent 常见的问题是:能把数据写进去,但不一定知道什么时候该加事务,什么时候要防重复提交,什么时候要考虑并发更新。

尤其是钱、库存、积分、额度、权限这种数据,不能靠“看起来没问题”。

4. 架构约束

成熟项目通常有自己的层次:controller、service、repository、domain、job、event、middleware。

这些分层有时候看起来啰嗦,但它们是在保护复杂度。

Agent 最容易犯的错,是为了完成当前任务,把逻辑塞到最近的文件里。短期很爽,长期很臭。

你让它加一个字段,它顺手在 controller 里查数据库、拼业务逻辑、发事件、写日志。功能可能过了,架构已经开始烂了。

5. 隐含需求

最难的是隐含需求。

比如某个字段不能删,因为老版本 App 还在用。某个接口返回结构很奇怪,因为有第三方客户接了。某段代码看起来重复,其实是为了绕开某个供应商的 bug。

这些东西可能不在 README 里,也不在 ticket 里,只在团队记忆里。

人类老员工都容易忘,更别说 Agent。

为什么框架越“高级”,Agent 反而越容易翻车

论文里还有个很有意思的观察:Agent 在 Flask 这类更小、更显式的框架上表现更好,在 FastAPI、Django 这类 convention-heavy 的环境里平均更差。

这不难理解。

显式框架像说明书。东西摆在明面上,路虽然土,但清楚。

惯例重的框架像城市交通系统。你得知道默认规则、生命周期、依赖注入、ORM 行为、middleware 顺序、配置约定。很多东西不写出来,但必须懂。

人类开发者靠经验记住这些坑。Agent 靠上下文推断。一旦上下文不够,或者任务太长,它就容易把框架当成普通代码库来改。

这也是为什么“把整个后端交给 Agent”听起来诱人,真正落地时很容易变成返工地狱。

它不是完全不会做。

它是会做 70%,然后在剩下 30% 里偷偷欠债。

而后端系统最贵的事故,往往就藏在这 30% 里。

今天的开发者社区,其实已经在找解法

有意思的是,今天的科技雷达里,不只 Constraint Decay 这篇论文在热。其他信号也在指向同一个方向:大家开始意识到,Agent 不能裸奔。

GitHub Trending 里,Understand-Anything 主打把代码变成交互式知识图谱,让你能探索、搜索、提问;codegraph 则强调本地预索引代码知识图谱,减少 token 和工具调用。

这类工具的潜台词很明确:Agent 想写好代码,得先更好地理解代码库。

Follow Builders 里,Peter Steinberger 提了一个我很喜欢的小建议:让 Codex 在做大重构时维护 scratch-log,记录它做过的决策、取舍、review fix,这样你事后能看它到底在哪里替你做了判断,也能看到你忘了交代什么。

这就是很实在的工程直觉。

不要幻想 Agent 永远正确。让它留下痕迹。

说白了,后端 Agent 工作流要从“给一句话,让它改完”变成:

  1. 先读项目约束
  2. 再写计划
  3. 执行时记录决策
  4. 每步跑测试
  5. 人类 review 关键边界
  6. 最后用静态检查和行为测试收口

这听起来麻烦,但后端开发本来就麻烦。想省掉这些步骤,最后通常会用线上事故还债。

我会怎么用 LLM Agent 写后端

如果是我,我不会让 Agent 直接“实现这个后端功能”。这个指令太宽了。

我更愿意把任务拆成几层。

第一步:让它复述约束

在写代码前,先让 Agent 输出它理解到的约束:

  • 涉及哪些模型和表?
  • 哪些接口会被影响?
  • 权限边界是什么?
  • 哪些状态不能跳?
  • 哪些现有测试必须保持通过?
  • 项目里类似功能是怎么写的?

这一步不是浪费时间。它是在检查 Agent 有没有看懂地图。

如果它连地图都说不清,后面写得越快越危险。

第二步:让它先找相似代码

后端项目通常有惯例。

不要让 Agent 凭空发挥,先让它找三个相似实现,再按现有风格改。

这比给它一堆抽象原则更靠谱。

原则容易被忘,附近代码不会骗人。

第三步:把权限、状态、数据一致性写成验收项

不要只说“实现创建订单”。

要写成这样:

  • 非项目成员不能创建订单
  • 重复请求不能生成两笔订单
  • 库存不足时必须失败,且不能扣减库存
  • 创建成功后必须写审计日志
  • 老接口返回结构不能变

这类约束越具体,Agent 越不容易自己脑补。

第四步:要求 scratch-log

大一点的改动,我会要求 Agent 维护一个临时日志,记录:

  • 改了哪些文件
  • 为什么这么改
  • 做过哪些取舍
  • 哪些地方不确定
  • review 后修了什么

这不是仪式感。这是给人类 review 留抓手。

没有日志,你只能看 diff 猜它的脑回路。有日志,你至少知道它在哪里开始飘。

第五步:让测试分层

后端任务不能只跑一个 happy path。

我会至少让它补三类测试:

  • 正常路径:功能能跑
  • 边界路径:权限、状态、重复请求、空数据
  • 回归路径:旧行为不变

如果项目允许,再加静态规则,比如 forbidden import、分层约束、ORM 使用约束、schema 兼容检查。

Agent 最怕这种白纸黑字的护栏。

这话听着像贬义,其实是好事。怕护栏,说明护栏有用。

真正的结论:Agent 不是程序员替身,而是需要管理的执行者

我不觉得 Constraint Decay 说明 AI coding 没戏。

相反,它说明 AI coding 正在进入更真实的阶段。

早期大家看的是:它能不能写代码?

现在更该问的是:它能不能在复杂约束里写对代码?如果不能,我们怎么设计流程,让它少犯错、早暴露、可追责?

这个问题比“模型 A 和模型 B 谁更强”重要得多。

后端开发的核心,不是把代码写出来,而是让系统在长期变化中不失控。LLM Agent 可以极大提高执行速度,但速度越快,约束越要清楚。

没有约束的 Agent,很像一个精力旺盛的新同事:活干得快,态度积极,还会主动帮你改一堆东西。

听起来很美。

直到你发现它把数据库事务删了,把权限校验绕了,把状态机改成了自由落体。

所以我的判断很简单:后端 AI 编程接下来真正值钱的,不是“更会写”的 Agent,而是“更不容易忘约束”的工作流。

谁能把上下文、约束、日志、测试、review 串起来,谁就能把 Agent 从玩具变成生产力。

否则,再聪明的模型也只是一个写代码很快、埋雷也很快的实习生。

参考链接

  • Constraint Decay: The Fragility of LLM Agents in Backend Code Generation:https://arxiv.org/abs/2605.06445
  • Hacker News 讨论:https://news.ycombinator.com/item?id=48256912
  • Understand-Anything:https://github.com/Lum1104/Understand-Anything
  • codegraph:https://github.com/colbymchenry/codegraph
  • Peter Steinberger 关于 scratch-log 的讨论:https://x.com/steipete/status/2058308112134635528