领域特定语言让大模型更可靠
速览
DSL(领域特定语言)通过约束输入输出格式,显著提升大语言模型在特定任务中的可靠性。它让模型更精准地理解需求,减少幻觉和形式错误。这种方法有望在金融、医疗等高风险领域得到广泛应用。
AI 深度解读
背景
大型语言模型(LLM)能够根据自然语言描述快速生成大量代码,甚至完整系统。然而,这种能力的前提是“意图”被精确表达,使得LLM能将其映射到编程模块。但现实中有两个关键限制:第一,前置规范无法穷尽所有设计决策;第二,设计是在实现过程中逐步发现的。因此,如何在LLM生成代码时保证可靠性成为核心问题。领域特定语言(DSL)提供了一种有效的约束机制,使LLM在有限、可验证的语法内工作,从而提升生成结果的准确性和可控性。
核心内容
前置规范的局限性
构建大型系统涉及大量细小的设计决策,这些决策无法在前期全部知晓,也无法完全由高层规范驱动。规范充其量是一个初始假设——真正的约束、权衡和边界情况是在实现过程中迭代发现的。原文将其称为“前置规范不可能性”(Upfront Specification Impossibility)。规范并非无用,但第一版规范应视为待修订的假设,而非最终蓝图。合理的做法是迭代:细化规范、生成代码、审查结果、将反馈带入下一轮。当每轮只产生微小、可审查的变化时,这种循环才有效。
设计通过实现被发现
审查代码与编写代码不同。在审查LLM生成的代码时,我们验证代码是否符合意图并寻找潜在问题,但很少被迫直面设计决策。而编写代码时,我们必须思考具体决策——如职责归属、暴露哪些边界以支持扩展——正是在这些决策中,设计才充分展现。编程语言和范式也影响设计洞见:函数式或面向对象设计会揭示不同层面的设计,并带来特定的习惯用法和模式。LLM在其中的角色有两个:在设计探索阶段,作为头脑风暴伙伴帮助发现抽象;在词汇建立后,作为自然语言接口使用这些抽象。
领域抽象与DSL
领域驱动设计(DDD)的核心思想是在代码中构建共享的领域概念模型,并使用该模型(即“通用语言”)来演进代码库,同时为团队提供思考和沟通的词汇。在此模型之上构建领域特定语言(DSL)往往非常有效——DSL是一种受约束的语法,用于表达领域的概念和操作。大多数开发过程就是构建领域模型并用它来演进系统。LLM的角色取决于领域模型是否已存在:若模型已存在,LLM可作为自然语言接口;若模型尚未建立,LLM可辅助探索设计空间。本文重点讨论DSL如何与LLM配合工作。
DSL与LLM高度适配的原因
DSL之所以与LLM配合良好,原因在于它们对少量上下文示例响应极佳。通用语言(如Java)有大量表达同一意图的方式,而DSL则消除了这种变化。给模型几个示例即可可靠生成正确语法。此外,前沿模型在训练时已大量接触PlantUML、Java流畅接口等DSL,并非从零开始。对于使用新颖DSL的小型模型,效果值得观察。
另一个关键优势是DSL几乎总带有确定性验证器(解析器、JSON Schema、类型检查器或编译器)。LLM作为智能体(在生成-检查循环中自主运行)时,可以生成候选代码,通过验证器检查,并根据错误信息修复——无需人工介入。错误信息以领域术语表述(如“不能在选择客户端之前选择动作”),而非深入生成代码的堆栈跟踪。DSL的工具集本身就是一个优秀的约束框架。
示例:用LLM生成图表丰富的PPT
在教授分布式系统时,需要制作包含逐步展示集群操作的幻灯片。UML序列图适合展示完整流程,但按步骤讲解时效果不佳。借助LLM,作者构建了一个工具:处理描述演示结构的YAML(包含PlantUML图引用),生成PowerPoint演示文稿。PlantUML图中标记了步骤,工具为每一步生成单独幻灯片。以下提示生成带步骤标记的PlantUML代码:
@startuml
actor Alice
box "Cluster" #lightblue
participant athens
participant byzantium
participant cyrene
end box
'[step] Alice -> athens: "title", "After Dawn"
'[step] athens -> athens: save()
note right of athens
state: title: After Dawn
end note
'[step] athens -[#red]x byzantium: "title", "After Dawn"
'[step] athens -> cyrene: "title", "After Dawn"
note right of cyrene
state: title: After Dawn
关键要点
- 前置规范不可信:规范只是初始假设,设计决策在实现中迭代发现,无法提前穷尽。
- 设计通过编写代码而非审查代码发现:审查代码很少迫使你做出设计决策,而编写代码则迫使你面对具体职责和边界。
- LLM的双重角色:在探索设计阶段作为头脑风暴伙伴,在词汇确定后作为自然语言接口。
- DSL通过约束语法提高LLM可靠性:DSL消除表达歧义,少量示例即可让LLM生成正确语法;通用语言则因变体过多而不可靠。
- DSL自带确定性验证器:LLM可自主生成候选、校验、修复,错误信息以领域语言表述,减少人工干预。
- DSL并非万能:其优势仅在DSL足够小且约束明确时成立;设计维护DSL及其语义模型有前期成本,适合经过良好分解的、真正受约束的DSL。
- 实用案例:利用PlantUML DSL和YAML构建工具,从自然语言描述生成带步骤标记的幻灯片,展示了DSL+LLM构建自定义工具的效率。
意义与影响
DSL与LLM的结合为软件工程中“可靠使用LLM”提供了实用路径。传统上,LLM生成代码的不可靠性源于意图模糊和语法多变,而DSL通过领域约束将生成空间限制在可验证的范围内,使LLM的输出更可预测、可修复。这一方法不仅适用于构建代码,也适用于LLM驱动的自动化工具(如文档生成、测试场景生成等)。随着LLM成为代码生成的核心工具,DSL的价值将进一步提升——它既能作为“真理之源”记录系统行为,又能作为LLM与人类之间的稳定接口
