# 实时的软件生成 **—— Prompt 编程打通低代码的最后一公里？**

PS：这也是一篇畅想，虽然经过了一番试验，依旧有一些不足，但是大体上站得住脚。

传统的软件生成方式需要程序员编写大量的代码，然后进行测试、发布等一系列繁琐的流程。而实时生成技术则是借助人工智能技术，让计算机自动生成代码，并直接运行，从而大大提高了软件生成的效率和质量。

而实时生成技术是一种借助人工智能技术让计算机自动生成代码，并直接运行的方法，大大提高了软件生成的效率和质量。实现实时软件生成的一种方式是 Prompt 编程，可以将自然语言转化为直接运行的软件，而无需生成中间的一次性过渡代码。

## 引子

从 2019 年，写了那篇《[**无代码编程](**[https://zhuanlan.zhihu.com/p/61288928](https://zhuanlan.zhihu.com/p/61288928))》开始，我也一直在思考，诸如于 [2021 年]([https://www.phodal.com/blog/frontend-strategy-2021/](https://www.phodal.com/blog/frontend-strategy-2021/)) 和 [2022 年]([https://www.phodal.com/blog/frontend-strategy-2023/](https://www.phodal.com/blog/frontend-strategy-2023/)) 的前端趋势总结。

先前，我并不看好当前的低代码/无代码方案，先我们来思考一下这个过程：

第一步，业务需求需要在人类脑海里转换一遍，转换为程序逻辑。

第二步，专业人员（如程序员）将这些逻辑转换到无代码系统中，生成应用并直接部署。

第三步，如果这个专业人员是程序员，那么新的需要变更时，他可能直接去修改源码。

而随着 ChatGPT 的进一步普及，你会发现你可以很容易将需求转化为**形式化格式**，进而转化而软件，从而使得软件开发的效率和速度大大提升。这种一次性代码的新型软件工程方法就是**实时软件生成**，便可以进入真正的 “**无代码**” 时代。

## Prompt 编程：真正的无代码

- Prompt 编程是实现实时软件生成的一种方式，可以将自然语言转化为直接运行的软件，而无需生成中间的一次性过渡代码。每当需要运行程序时，只需要再运行一下 prompt 或者**触发词**，就可以直接运行起某个软件。

我们尝试从编写 Prompt 的经验里，归纳出一套适合于 Prompt 编程的逻辑，方便于大家理解。Prompt 编程有以下特性：

1）从需求到代码的直接转换；

2）代码只是临时产物；

3）次序化的分解框架。

这种一次性代码的新型软件工程方法可以进入真正的 “无代码” 时代。

### Prompt 编程特性 1：从需求到代码的直接转换

用户只需要简单输入自己的需求就能够生成符合要求的软件，无需具备专业的编程技能。示例：

```yaml
我们来玩一个 mul 游戏，当我说 mul 开头，并输入数字时，你应该计算结果。示例："""当我输入：mul 4，你应该输出：16"""
```

在这个游戏里，我们使用：

1. **触发词**。mul 作为街头暗号，告诉程序你要按这个步骤走。类似于编程里的函数名。
2. 示例作为**输入和输出**。即预期输入某个结果时，输出我们想要的结果。

而在其实，已经是一个函数该有的样子：**输入、输出、关系**。值得注意的是，在简单的问题下，只要我们给定了示例，那么 AI 就会根据示例去计算输入和输出，不关注中间的处理逻辑。

接下来，再看一个隐含性编程的示例，依旧我们还是玩个游戏：

```yaml
我们能玩一个名为 kfc 的谜语创作游戏，当我说 "kfc"，你写一个谜题，要求：

1. 不少于 200 字
2. 谜题不能出现肯德基和星期四
3. 正文可以夹杂小语种语言，如 """他说：Ciao"""
4. 谜底必须是 ###原来今天是肯德基疯狂星期四!!!###

格式类似于：

"""
...
"""
```

这个例子已经相当的自由，我们也不关心中间的处理逻辑，只关注于结果。

### Prompt 编程特性 2：代码只是临时产物

还记得在 [Prompt 编写模式]([https://github.com/phodal/prompt-patterns](https://github.com/phodal/prompt-patterns)) 中的 `wula` 游的例子吗？在 wula 游戏里，用户只需要输入问题，ChatGPT 便会自动给出代码、代码解释和文章。诸如于：`wula: 头共10，足共28，鸡兔各几只？`，那么  AI 便会开始自动开始二次编程：

```yaml
这是一个鸡兔同笼问题，题目给出了头的数量和足的数量，要求求出鸡和兔的数量。通常解决这个问题的方法是，假设鸡和兔的数量分别为 x 和 y，那么可以列出如下方程组：

x + y = 10
2x + 4y = 28

解方程组可以得到 x=4，y=6，即鸡有4只，兔有6只。
鸡兔同笼问题的 JavaScript 代码实现

// 计算鸡兔数量的函数
function calcAnimals(heads, legs) {
.....
```

在传统的体系之下，我们要关注于中间生成的 **JavaScript 代码**、**代码能不能运行**等等，所以我们在设计  wula 的时候，要求了：

```yaml

第二步. 代码编写：你需要用 JavaScript 编写解决这个问题的代码，并输出对应的代码，并介绍一下你的代码（不少于 200 字）。

第三步. 代码执行：你需要作为 JavaScript Console 执行第二步写的代码，如果没有给出测试数据，你需要自己随机生成测试数据，并将这些数据输入到代码中进行计算。
```

但是，回到第一个例子里，我们并不需要 JavaScript 代码，我们不需要任何代码。ChatGPT 可以为我们生成这部分的程序！！！

唯一限制我们的是：

1. ChatGPT 的上下文能力
2. 我们的 Prompt 编写能力

而在复杂的场景之下，Prompt 编写需要足够的形式化，才能让机器理解，也因此，我才把它称为 Prompt 编程。

### Prompt 编程特性 3：次序化的分解框架

仅就当前而言 AI 的上下文能力，限制了 AI 解决复杂问题的能力。所以，它无法**一次性解决**复杂、中大型的问题，甚至于小型项目都存在一定的难度。同样的，对于人类来说，也存在这样的局限性，只是人类的问题是**领域知识有限**。

步骤详细见：[DDD Sample]([https://github.com/phodal/prompt-patterns/blob/master/ddd.sample.md](https://github.com/phodal/prompt-patterns/blob/master/ddd.sample.md))

所以，作为编程核心的人类大脑，需要为机器设计一下工序，即每一步应该怎么做。所以，这个时候，就回到了 **DDD**（领域驱动设计）擅长的问题：

```yaml
我们来定义一下 DDD 游戏的步骤，一共有 6 个步骤，步骤如下：

"""
第一步. 拆解场景。分析特定领域的所有商业活动，并将其拆解出每个场景。
第二步. 场景过程分析。选定一个场景，并使用 "{名词}已{动词}" 的形式描述过程中所有发生的事件，其中的名词是过程中的实体，其中的动词是实体相关的行为。
第三步. 针对场景建模。基于统一语言和拆解出的场景进行建模，以实现 DDD 设计与代码实现的双向绑定。
……
"""
```

对于每一步，我们只要限定好我们的输出（Output），那么在给定输入的情况下，AI 就能给出我们想要的（也有可能跑偏了）结果：

```yaml
ddd 第二步：场景过程分析。下面是每个场景的过程分析：
用户注册和登录
    用户注册：用户填写注册信息，系统验证信息合法性，生成用户账户并保存。
    用户登录：用户输入账户名和密码，系统验证账户密码，登录成功后跳转到用户中心。

商品展示和搜索
    商品列表展示：系统从数据库中获取商品列表并展示，用户可以进行商品分类和排序。
    商品搜索：用户输入关键词进行商品搜索，系统根据关键词从数据库中获取商品列表并展示。
……
```

在这时，我们就可以引导 AI 生成到 API 粒度：

| API名称 | 请求方法 | 请求路径 | 请求体 | 响应体 | 描述 |
| --- | --- | --- | --- | --- | --- |
| 创建商品 | POST | /api/products | 商品信息 | 创建的商品信息 | 创建一个新的商品 |
| 获取商品列表 | GET | /api/products | 无 | 商品列表信息 | 获取所有的商品列表 |
| 获取单个商品信息 | GET | /api/products/{id} | 无 | 单个商品信息 | 根据商品 ID 获取单个商品信息 |
| 更新商品信息 | PUT | /api/products/{id} | 更新的商品信息 | 更新后的商品信息 | 根据商品 ID 更新商品信息 |
| 删除商品 | DELETE | /api/products/{id} | 无 | 无 | 根据商品 ID 删除商品信息 |

也可以生成代码，甚至于在上下文足够强大的时候，也是能够直接运行和自动优化的。

### Prompt 编程特性 4：语言即语言

DSL（领域特定语言）是指专门针对某个特定领域的语言，通常具有严格定义的语法和规则，并且可以用来描述该领域中的问题、概念、过程和数据等。而经过形式化处理的自然语言可以被视为一种 DSL，因为它们具有明确定义的语法和规则，可以用来描述特定领域中的问题、概念、过程和数据等。

所以，简单来说如果我们限定了**输入和输出的格式**，那么我们的自然语言也是一种 DSL。这就意味着，限制我们进行次序化的关键就是形式化的抽象。

而，我们也可以通过编写 Prompt 来生成新的 DSL：`帮我设计一个 DSL，用来写函数`

```yaml
好的，以下是一个简单的DSL设计，可以用来写函数：

1. 函数定义
在这个段落中，定义一个函数，包括函数名、参数和函数体等。
```
函数：
  函数名：
    描述函数的名称
  参数：
    参数1：
      描述参数1的名称和类型
    参数2：
      描述参数2的名称和类型
    ...
  函数体：
    描述函数的实现细节
```

2.。。。
```

简单来说，我们可以通过 Prompt ：

1. 借用 Prompt 设计自然语言式的 DSL
2. 将自然语言式的 DSL 融入 Prompt 
3. 使用次序化的方式来实现 Prompt 自我编程

进而，我们就得到了一个可以自举的 Prompt。

## Prompt 编程 Demo：引导 ChatGPT 创建语言与建模

说了，这么多我们来看一个结合我之前设计的 DSL 示例，即 [Fklang](https://github.com/feakin/fklang)。

### 阶段一：让 ChatGPT 与你学 DDD

在 Fklang 中，我们采用下面的形式来表示 ContextMap：

```yaml
ContextMap TicketBooking {
    Reservation -> Cinema;
    Reservation -> Movie;
    Reservation -> User;
}
```

但是，自己设计这样的 DSL 太累了，可以让 ChatGPT 来设计。在你开始之前，需要确认一下  ChatGPT 中的 DDD、DSL、事件风暴等的概念是否与你理解的一致。

第一个问题：`设计一个 DSL 来表示 DDD 里的 Context Map 。`

```yaml
ContextMap:
    Subdomain { Name: [subdomain_name] }
        [CollaboratesWith: [other_subdomain_name], ...]
        [ConformistWith: [other_subdomain_name], ...]
        [Antagonizes: [other_subdomain_name], ...]
    ...
```

第二个问题：用这个 ContextMap DSL 对在线电影订票系统建模。对于其它问题也是相似的。

第三个问题：设计一个 DSL 来描述事件风暴的 事件建模 吗？

第四个问题：能用 事件建模的 DSL 来表示用户购票的流程吗？

一旦，我们觉得设计跑歪了，就可以重新设计。如果下是生成的复杂的 DSL：

```yaml
EventStorming:
    Domain { Name: "电影订票系统" }
        Event { Name: "用户提交订单" }
            Triggered By: "用户选择电影、场次、座位，确认订单"
            Description: "用户提交订单，包括所选电影、场次、座位等信息"
            Actors: ["用户"]
            Action: "将用户提交的订单信息保存到订单数据库中"
            Outcome: "订单状态被标记为已提交"
```

第五个问题……

剩下的你都会了，不会的，可以问 ChatGPT。

### 阶段二：完整设计 + DSL 生成

第一个问题：能总结一下，我们这里用 DDD 设计 购票系统到落地代码的过程吗？

第二个问题：能为每个过程，设计一个合理的 DSL 吗，并展示他们？

展示一部分神奇的 DSL：

```yaml
generate java-code from-domain-model
    target-package: com.example.movieticket.order
    source-model: order-domain-model

    service-mapper 订单服务映射
        map-method: 查询电影
        to-class: MovieRepository
        to-method: findBySchedule

        map-method: 查询座位
        to-class: SeatRepository
        to-method: findByRowAndColumnAndStatus
```

现在，有意思的地方来，有了上面的一系列 DSL 之后，我们就可以接入到代码系统中。

### 阶段三：代码生成与低代码

只要 ChatGPT 上下文能力足够强壮，或者支持 LoRA 式的插件模式，我们就能实现从需要到现有的任何系统中。

第一个问题：为电影订票系统设计一个 DDD 风格 Java 工程的代码目录结构。

第二个问题：结合 Spring 设计一下购票流程的 RESTful API

……

有点懒，就先这样吧。后面的部分，就可以结合 GitHub Copilot 去实现了。

### 小结

结合 Prompt 编程，低代码到了一定的成熟度，我们就可以发现更好玩的东西：实时的软件生成

## 实时的软件生成：自然语言即 Prompt，Prompt 即代码

实时软件生成核心思想是，通过算法和机器学习来自动生成代码，让计算机根据用户需求，快速生成符合要求的软件。这种技术能够自动化完成代码的编写、测试、发布等流程，大大缩短软件开发周期，降低了开发成本，提高了开发效率。

当我们想构建这样一软件用于实时生成软件时，它需要具备以下的特征：

### 特征 1：自然语言即语言，语言即软件

即如上面的 Prompt 编程所述，可以通过设定层层转换，直接将需求直接转换为软件。

### 特征 2：生成式的软件架构

软件本身不需要架构，架构是 AI 自动生成和调整的。

### 特征 3：自底向上生成

现有的语言本身需要 REPL 环境、操作系统、编程语言、语言底层库、库等一系列软件，对于 AI 而言，他能学习这些通用能力，自操作系统底层一样，逐步往上构建出软件的运行环境，以及软件本身 。

## 当前的挑战

对于当前而言，我们还存在些挑战：

- 现行组织架构难以支撑内部 ChatGPT。如内部权限、架构等的管理
- 通用大模型无法满足。领域特定能力有限，需要构建 LoRA 以更好的支持。
- 细节能力实现较差。在编程实现上，远不如 GitHub Copilot

当然了，受限于个人能力，可能还有别的一些挑战。

## 总结

本文介绍了 Prompt 编程的特点和实时软件生成的核心思想。Prompt 编程是一种次序化的分解框架，可以让机器根据用户需求自动生成代码。实时软件生成技术可以大大提高软件生成的效率和质量，同时也让软件开发变得更加简单。然而，现有组织架构难以支撑内部 ChatGPT，上下文能力有限，细节能力实现较差，这些都是实现 Prompt 编程和实时软件生成的难点。
