规范驱动开发:使用Markdown作为AI编程语言

本文介绍了一种创新的开发方法,将Markdown作为编程语言,通过GitHub Copilot将规范编译成Go代码。这种方法实现了更清晰的规范、更快的迭代速度,并消除了上下文丢失问题,为AI辅助开发提供了新思路。

GitHub Brain MCP服务器

我最近完全使用Markdown编写了我的最新应用程序,并让GitHub Copilot将其编译成Go代码。这带来了更清晰的规范、更快的迭代速度,并且不再有上下文丢失问题。

传统AI编码代理工作流程

使用GitHub Copilot等AI编码代理的常规工作流程很简单:“编写执行X功能的应用程序A”。你从那个种子开始,然后迭代:“添加功能Y”,“修复错误Z”。这很有效,直到代理忘记你的应用程序目的或过去的决定。

如果你刚接触AI编码代理,这种变化很微妙。突然之间,代理要求你重复已经解释过的事情,或者建议忽略你先前指令的更改。有时它会忘记某个功能存在的原因,或者提出与早期选择相矛盾的解决方案。

规范驱动开发方法

一些AI编码代理试图通过支持自定义指令文件来解决这个问题。例如,GitHub Copilot支持copilot-instructions.md。你可以将应用程序的目的和设计决策放在这个Markdown文件中,GitHub Copilot每次生成代码时都会读取它。

但当我匆忙编码时,经常忘记在要求GitHub Copilot执行操作后更新copilot-instructions.md。将相同的信息同时放入聊天提示和指令文件中感觉是多余的。

这让我思考:如果我在Markdown指令文件中"编写"整个应用程序会怎样?

实际应用:GitHub Brain MCP服务器

对于我的最新宠物项目——GitHub Brain MCP服务器,我尝试了这种方法:在Markdown中编写应用程序代码,并让GitHub Copilot将其编译成实际的Go代码。结果,我很少直接编辑或查看应用程序的Go代码。

这个过程应该适用于任何AI编码代理和编程语言,不过我将使用VS Code、GitHub Copilot和Go作为示例。

设置:开始所需的关键文件

有四个关键文件:

1
2
3
4
5
6
.
├── .github/
│   └── prompts/
│       └── compile.prompt.md
├── main.go
├── main.md

main.md:AI编码代理规范

main.md是应用程序的实际源代码:Markdown指令文件。当我需要添加功能或修复错误时,我编辑此文件。

这是示例应用程序main.md的开头:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# GitHub Brain MCP服务器



## CLI



## pull

- CLI参数和环境变量解析为`Config`结构体:
  - `Organization`:组织名称(必需)
  - `GithubToken`GitHub API令牌(必需)
  - `DBDir`SQLite数据库路径(默认:`./db`
- 一致使用`Config`结构体,避免多次读取环境变量
- 拉取项目:存储库、讨论、问题、拉取请求、团队
- 在控制台输出中使用`log/slog`自定义记录器显示最后5条带时间戳的日志消息

这是示例应用程序main.md的另一个片段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
### 讨论

- 对每个启用讨论功能的存储库查询讨论
- 在拉取第一页之前,从数据库中记录最近存储库讨论的`updated_at`时间戳

```graphql
{
  repository(owner: "<organization>", name: "<repository>") {
    discussions(first: 100, orderBy: { field: UPDATED_AT, direction: DESC }) {
      nodes {
        url
        title
        body
        createdAt
        updatedAt
        author {
          login
        }
      }
    }
  }
}
  • 如果存储库不存在,从数据库中删除该存储库及所有相关项目并继续
  • 按最近updatedAt排序查询讨论
  • 当遇到updatedAt早于记录时间戳的讨论时停止拉取
  • 按主键url保存或更新
  • 保留讨论markdown正文
1
2
3
4

这实际上是用Markdown和纯英语编程:存储变量、循环和逻辑条件。你可以使用所有常用关键词——if、foreach或continue。它是结构化和声明式风格的混合,使用Markdown链接[]()进行导入。

数据库模式也用Markdown编码:

数据库

SQLite数据库位于{Config.DbDir}/{Config.Organization}.db中(如果需要则创建文件夹)。避免事务。立即保存每个GraphQL项目。

table:repositories

  • 主键:name

  • 索引:updated_at

  • name:存储库名称(例如repo),不带组织前缀

  • has_discussions_enabled:布尔值,指示存储库是否启用了讨论功能

  • updated_at:最后更新时间戳

1
2
3
4

### compile.prompt.md:AI编码代理提示

compile.prompt.md使用GitHub Copilot的提示文件格式。这个可重复的提示告诉代理将main.md编译成main.go。这是示例应用程序的compile.prompt.md:

mode: agent

  • 更新应用程序以遵循规范
  • 使用VS Code任务构建代码。避免要求我手动运行go buildgo test命令。
  • 获取每个使用库的GitHub主页以获取文档和示例。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

我保持这个提示简单。毕竟,真正的信息在main.md中。这个示例使用GitHub Copilot的格式,但保持简单使其可移植到其他AI编码代理。

## 整合所有内容的工作流程

开发循环很简单:


2. 要求AI编码代理将其编译成Go代码
3. 运行和测试应用程序。如果某些功能不如预期工作,更新规范
4. 重复

在GitHub Copilot for VS Code中,使用/命令调用提示。

对于较小的规范,GitHub Copilot通常会自动捕获更改。随着规范的增长,我通过附加"focus on <the-change>"将其推向正确方向。

## 编码

在main.md中编码有时比直接编写Go更困难。你必须清楚地描述你想要什么,这可能是软件开发中最难的部分。幸运的是,你可以使用GitHub Copilot来帮助解决这个问题,就像你可能每天使用它帮助Go代码一样。

## 代码整理

main.md可能像任何代码一样变得混乱。为了帮助解决这个问题,你可以要求Copilot清理它。这是示例应用程序的lint.prompt.md:

mode: agent

  • 优化应用程序规范以提高清晰度和简洁性
  • 将英语视为编程语言
  • 最小化同义词数量 - 即pull/get/fetch。坚持使用一个术语。
  • 删除重复内容
  • 保留所有重要细节
  • 不要用这个修改Go代码。仅优化Markdown文件。
  • 不要修改此提示本身。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

与compile.prompt.md一样,我使用/命令调用此提示。AI编码代理整理main.md,如果结果看起来不错,我可以用compile.prompt.md将其编译成Go。

## 结束思考

使用这个工作流程几个月后,以下是我的观察:

- 它有效!并且随着Copilot的每次代理更新变得更好
- 随着main.go的增长,编译速度变慢。我接下来想做的是修改规范,将编译后的代码分成多个模块——通过添加"将每个##部分分解成自己的代码模块"
- 测试?我还没有尝试添加测试。但即使使用规范驱动的工作流程,测试仍然至关重要。规范可能描述了预期行为,但测试验证它

我接下来想尝试的其他事情?丢弃所有Go代码,并用另一种语言从头开始重新生成应用程序。新代码会立即工作吗?

这个领域的快速进展确实令人鼓舞,我希望我的实验性工作流程能给你一些实用的想法来尝试。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计