软件设计大师Martin Fowler7月14日在自己的网站上发文,抛出一个听起来很实用的判断:想让大语言模型写代码写得靠谱,别让它写Java或Python这种通用语言,改让它写DSL(领域特定语言)——一种语法收窄、只服务某个具体领域的"小语言"。文章用他自己搭的分布式系统测试框架Tickloom做例子,看起来很有说服力。但仔细拆开看,这篇文章证明的东西,比它听起来的结论要窄得多。

Tickloom案例:约束语法真的管用

Fowler的逻辑不难理解。SQL是查数据库的DSL,PlantUML是画图的DSL,Kubernetes的YAML是描述云基础设施的DSL——这些语言语法变体少,LLM只要看几个例子就能稳定学会怎么写。反观Java这种通用语言,同一个意图有一百种写法,模型容易写岔。

他给出的Tickloom是给分布式系统测试用的框架,核心设计是把"谁是client、连到哪个server、按什么步骤跑"这类信息用Java的链式构建器(比如QuorumStepBuilder.scenario(...).servers(...).client(...))串起来,顺序被宿主语言的编译器强制锁死——想在没选client之前就写action,编译直接报错。这套东西真正有价值的地方,是它天生带着验证器:解析器、类型检查器、编译器全都现成,agent生成一段代码、跑一次检查、拿着报错自己修,不需要人工天天盯着审查。

这个思路本身站得住脚,也呼应了Fowler更早提出的"预先规格不可能性"——大系统的设计决策没法一次性写进规格书,只能在实现中一点点被发现。DSL在这个过程里演两个角色:先是帮人一起琢磨设计词汇的"脑暴搭子",词汇定下来之后,变成把英文请求翻译成合法语法的接口。

"可靠"这个词,被用得太宽了

问题出在"可靠"两个字上。文章举的例子全是"能不能编译通过、能不能跑起来",但这只是可靠性的第一层。真正决定一个自动生成的分布式协议能不能上生产的,是它有没有语义正确——选对了client、写对了断言、业务规则没搞反。这一层,DSL的编译器根本管不到。

一段"合法但错误"(wrong-but-executable)的代码,语法上挑不出毛病,逻辑上却可能选错了quorum的判断条件,或者在last-writer-wins的时间戳比较里写反了大小于号。编译器不会报错,因为它压根不知道"对"是什么样子。Fowler文章里反复强调的"生成-校验-修复"循环,对这类错误其实是失灵的——它能抓语法错,抓不住会错意的错。

外部已经有一些数据能说明这道鸿沟有多宽。一项覆盖39个模型的评测发现,只有26个模型能在允许的重试次数内产出至少一份语法合法的输出,剩下十几个连这道最低门槛都过不去;而在语法合法的那部分输出里,语义完整性和一致性在不同模型间差异依然明显,研究者明确说这是还没解决的难题。Hacker News上一篇讨论"LLM给DSL设计者出了道难题"的帖子积累了151条评论,争论焦点也不在"DSL好不好用",而在新语言训练数据稀缺、学习成本高、抽象容易泄漏这些更根子的问题上。

  • 风险.编译器验证的是语法合法,不是业务正确;"合法但做错事"的代码不会触发任何报错,也就不会被自动修复循环拦下

下面这张图把Fowler的论证止步于哪一层画得更清楚:

"可靠"拆开看,有四层 ① 语法合法 符合DSL的拼写和结构规则 Fowler已证明 ② Schema合规 字段类型、必填项符合定义 Fowler已证明 ③ 可执行 能跑起来,编译器/解释器不报错 Fowler已证明 ④ 语义正确 选对client、写对规则,真正做对事 未被验证 39个模型评测:仅26个能过①,④层至今缺系统数据

表达力越强,DSL越容易变回它想避开的样子

Fowler文章自己也承认了一句实话:这套方法只在DSL"足够小、足够受限"时才成立,设计和维护一套语言的语义模型、诊断信息、工具链,前期成本是真实存在的。这句话说得轻描淡写,但分量不小。

现实中DSL很少能一直保持"小"。业务需求会不断加条件、加转义机制、加扩展点,一套原本三五个关键字就能说清的语言,用上一两年往往会长出分支逻辑和特殊情况处理——而这恰恰是它当初想绕开的复杂度。等到这一天,"少量示例就能教会模型"的优势也跟着松动。

对正在搭LLM-agent自动编码流水线的团队来说,更现实的问题不是"要不要上DSL",而是要不要把"合法"和"正确"分开验证:语法层交给编译器兜底没问题,但语义层的把关,目前还得靠人工审查或者专门写的业务规则检查器,agent自己修不了。设计一套类型化API未必比设计DSL便宜多少,但它至少不需要额外维护一套语法和工具链——这是选择前该算清楚的一笔账,而不是案例好看就直接抄作业。