从命令式到声明式
一个反复发生的转变
从汇编到 C,从 C 到 SQL,从 jQuery 到 React,从 Makefile 到 Terraform——软件工程的历史中反复出现同一个转变:从命令式到声明式。每一次都在做同一件事——把"怎么做"的负担交给系统,让开发者只需要声明"要什么"。
LLM 应用开发正在经历同样的转变。从手工编写自然语言 prompt(命令式)到用类型系统和代码结构声明期望输出(声明式),这是软件工程史上同一个模式的最新一次。
几个你已经见过的例子
C 语言让你写 a = b + c 而不是四条汇编指令。寄存器分配、指令调度、内存布局——这些决策被交给了编译器。看起来微不足道,但这就是一次转变:程序员从"怎么加"变成了"要什么结果"。
SQL 走得更远。SELECT name FROM companies WHERE revenue > 1000000 没有告诉数据库用哪个索引、按什么顺序扫描、在内存还是磁盘排序。导航式数据库时代,这些都是程序员的活。SQL 之后,查询优化器接管了这些决策——而且比绝大多数程序员做得更好。
React 把同一件事做在了 UI 领域。jQuery 时代你操作 DOM:找到元素、改属性、加类名。React 之后你只描述"给定状态,UI 应该是什么样",具体怎么更新 DOM 由 reconciliation 算法决定。
回头看这四十年,每一次转变都在做同一件事——把"怎么做"的决策从人转移到系统,同时引入某种约束语言来表达"要什么"。而每一次转变都有三个相似的特征:
- 抽象的代价是可控的。C 比手写汇编慢几个百分点,不是几倍。
- 新引入的约束反而增加了可靠性。类型系统看起来是限制,实际上是保护。
- 旧范式没有消失,只是退缩到了真正需要精细控制的地方。性能关键路径上仍然可以写内联汇编。
把 LLM 放进这条规律
把 LLM 提示工程放进同一张表,位置一目了然:
| 领域 | 命令式形态 | 声明式形态 | 被委托的决策 | 约束语言 |
|---|---|---|---|---|
| 系统编程 | 汇编语言 | C 语言 | 寄存器分配、指令调度 | 类型系统和函数签名 |
| 数据查询 | 导航式数据库 | SQL | 索引选择、扫描策略 | 关系代数语法 |
| UI 开发 | jQuery DOM 操作 | React 组件 | DOM diff 和增量更新 | props 和状态的类型定义 |
| 基础设施 | 手动配置服务器 | Terraform / K8s | 资源创建顺序、状态收敛 | HCL / YAML 资源声明 |
| LLM 提示工程 | 自然语言 prompt | 类型化结构声明 | 推理路径和输出格式 | Pydantic / JSON Schema |
约束语言越精确,系统行为越可预测。
LLM 提示工程中的命令式遗留
当前主流的 prompt 工程实践还是命令式的。一个典型的命令式 prompt:
你是一个专业的文档分析师。请按照以下步骤分析给定的文档:
第一步:通读全文,识别文档的主题和类型。
第二步:提取文档中的所有关键实体(人名、组织、日期、金额)。
第三步:分析实体之间的关系。
第四步:基于以上分析,生成一份结构化摘要。
摘要应包含以下部分:
- 文档概述(不超过 3 句话)
- 关键实体列表
- 关系图谱描述
- 结论和建议
请确保输出为 JSON 格式。这段 prompt 的问题和汇编语言时代、jQuery 时代面临的问题结构上是同一类:控制流依赖自然语言理解而非硬约束,输出契约是愿望而非约束,整段 prompt 不可组合也不可独立测试。开发者的抽象层级搞错了——本该由系统处理的细节,得靠开发者自己管。
声明式提示工程的方向
声明式提示工程的核心转变是:用类型定义替代过程描述,用结构约束替代自然语言指令。这个转变对应两个核心概念,构成本章后续内容的主线:
Code as Prompt。 Pydantic 模型同时充当类型定义、语义指令和输出验证器。一处定义,三处生效。代码本身就是 prompt,不再需要一段独立的自然语言文本来描述"请输出什么格式"。下一篇展开这个概念。
Schema as Workflow。 模型的字段排列定义了 LLM 的推理路径。字段顺序就是思维链的步骤,字段之间的依赖声明就是推理步骤间的逻辑连接。Schema 不仅描述输出的结构,同时编排了产生这个输出的推理过程。第三篇展开这个概念。
命令式的保留地
声明式范式不是要消灭命令式。选哪种范式,看输出空间的结构特征就能判断:
| 任务类型 | 输出空间 | 结构稳定性 | 推荐范式 | 理由 |
|---|---|---|---|---|
| 结构化数据提取 | 有界 | 稳定 | 声明式 | 输出结构明确,字段和类型已知,声明式约束可以最大化可靠性 |
| 创意写作 | 开放 | 未知 | 命令式 | 输出空间本质上是开放的,类型约束会扼杀创造性 |
| 客服意图分类 | 有界 | 稳定 | 声明式 | 输出是有限枚举集合,Literal 类型是天然的表达 |
| 代码审查 | 有界 | 在变化 | 混合 | 问题列表的结构是确定的(声明式),但每个问题的描述需要自由表达(命令式) |
判断的关键就两点:输出能不能穷举,结构稳不稳定。输出有界且结构稳定时,声明式范式的优势最大;输出开放且结构未知时,命令式范式的灵活性不可替代。