用 Python 自动创建 Markdown 表格 - 每天5分钟玩转 GPT 编程系列(4)

用 Python 自动创建 Markdown 表格 - 每天5分钟玩转 GPT 编程系列(4)

·

5 min read

1. 他们居然问我要 Prompts

早几天我用 GPT-4 写了一个 Golang Worker Pool 程序,测试了一把居然性能爆表,然后“他们”就问我要完整的 prompts,想看下到底是 GPT-4 写出来的 GoPool,还是我自己手撕的。(我哪有手撕的本事,你们还真是看得起我。)

GoPool 是一个 95% 代码用 GPT-4 生成的高性能、功能强大且简单易用的 Golang Worker Pool 开源库,详细介绍可以参考这篇文章:

好吧,那我就搞一个网站,把 GoPool 开发过程中用到的 prompts 以及对应的 commits 都列在一起,开源出去!

首先我需要在网页里加这样一张表格:



没错,表格的原始文本得是 Markdown 格式的,然后被转成 HTML(我总不能手撕 HTML 表格吧),放到 pro.devchat.ai

这个表格对应的 Markdown 最终会长这样:

混乱不?抓狂不?我反正写了一行就放弃了:

| Commit                                                                                                                              | Commit Hash | Author                                       | Prompts with GPT                                                          | 给 GPT 的 Prompts                                                             |
| :---------------------------------------------------------------------------------------------------------------------------------- | :---------- | :------------------------------------------- | :------------------------------------------------------------------------ | :---------------------------------------------------------------------------- |
| [Add README.md and go.mod for GoPool project](https://github.com/devchat-ai/gopool/commit/a6315922a026b4290b0b1cd54e95b25e93538047) | a631592     | [Daniel Hu](https://github.com/daniel-hutao) | [Prompts with GPT](./commits/a6315922a026b4290b0b1cd54e95b25e93538047.md) | [给 GPT 的 Prompts](./commits/a6315922a026b4290b0b1cd54e95b25e93538047_zh.md) |

写完一行,老眼昏花,内心久久不能平静。这种“重体力活”怎么能忍?

你想象一下我要到 GitHub 上的 GoPool 项目 commits 列表里,看着接近30个 commits,一个个去摘抄这些信息:

  1. Commit message 第一行;

  2. Commit hash 前7位;

  3. Author 信息;

  4. 完整的 commit 地址;

然后在本地创建和 commit 地址对应的一个个 Markdown 文件,类似:

最后还要在这些 Markdown 文件里将这个 commit 对应的 Prompts 填进去,让大家看到这个效果:



真他喵不是人干的活。赶紧冲一杯咖啡提提神,搞不完今天下不了班了。

有事 GPT 干,没事干 GPT,有事 GPT 干,没事干 GPT,有事 GPT 干,没事干 GPT……

对呀,脏活累活,一律祭出 GPT!

2. 让 GPT-4 来写代码

直觉上这个任务适合用 Python 脚本来实现。不过我并不熟悉 Python,也就是说如果 GPT-4 给的 Python 代码能用,我就用。反之不能用,我就不得不自己用 Golang 来写了。

下文通过 DevChat 和 GPT-4 交互。DevChat 是一款开源的 VS Code 插件,默认对接 GPT-4,现在还在公测阶段,可以通过这个地址免费注册一个 key,然后不需要“ke xue 上网”就能用上 GPT-4 了。

关于 DevChat 的详细介绍欢迎跳转这篇文章:

2.1 我对 DevChat 说

如上图所示,在问这个问题的时候我加了一个“上下文”,也就是一行 Markdown 表格示例。

完整的问题如下:

我需要你写一个 python 脚本,脚本中要实现的功能是拉取GitHub上特定项目的Commit记录,然后写入本地特定文件中。

  1. 比如项目的 org 是 devchat-ai,name 是 gopool,那么你要找的项目是 github.com/devchat-ai/gopool

  2. 你要获取这个项目的main分支的所有 commits 历史,按照时间顺序依次写入本地的 devchat-ai/gopool/index.md 的中,但是你要考虑脚本重复执行需要幂等,所以你应该追加内容,而不是覆盖。

  3. 我已经将 index.md 的例子发给你,你不需要管里面的开头描述部分,只需要在文件后半部分的 Markdown 表格上操作。

2.2 DevChat 回答

要实现这个需求,我们需要做以下几步:

  • 使用 GitHub API 来获取特定项目的 commit 历史记录。

  • 解析 API 返回的数据,提取我们需要的信息,如 commit hash,author 等。

  • 将这些信息以 Markdown 表格的形式追加到本地的 index.md 文件中。

这里有一个 Python 脚本的示例,它使用了 requests 库来发送 HTTP 请求,使用 pandas 库来处理和写入 Markdown 表格。如果你的环境中没有这两个库,你可以使用 pip 来安装它们:

pip install requests pandas

然后,你可以使用以下的 Python 脚本来实现你的需求:

import requests
import pandas as pd
from typing import Dict, List

def get_commits(org: str, repo: str) -> List[Dict[str, str]]:
    url = f"https://api.github.com/repos/{org}/{repo}/commits"
    response = requests.get(url)
    response.raise_for_status()
    commits = response.json()
    return [
        {
            "Commit": commit["commit"]["message"],
            "Commit Hash": commit["sha"],
            "Author": commit["commit"]["author"]["name"],
        }
        for commit in commits
    ]

def append_commits_to_file(commits: List[Dict[str, str]], file_path: str) -> None:
    df = pd.DataFrame(commits)
    with open(file_path, "a") as f:
        f.write(df.to_markdown(index=False))

def main():
    org = "devchat-ai"
    repo = "gopool"
    file_path = "devchat-ai/gopool/index.md"
    commits = get_commits(org, repo)
    append_commits_to_file(commits, file_path)

if __name__ == "__main__":
    main()

这个脚本首先从 GitHub API 获取 commit 历史记录,然后将这些记录追加到本地的 index.md 文件中。注意,这个脚本只会获取最近的 30 条 commit 记录,因为 GitHub API 的默认分页大小是 30。如果你需要获取更多的记录,你需要处理 API 的分页。

此外,这个脚本没有处理 API 的错误和异常,你可能需要根据你的需求来增加错误处理和异常处理的代码。

管它三七二十一,对着操作一波再说。运行后我得到了这个结果:

一口老血啊…… 克制,不动怒,继续“友好”沟通。

2.3 我又对 DevChat 说

hash 我只需要保留7位,Commit Message 只需要保留第一行。最后的结果需要是这样的:

| [Add GoPool and Worker structures](https://github.com/devchat-ai/gopool/commit/ee0b38ba85830d07046145583c2a568905d9839f) | ee0b38b | [Daniel Hu](https://github.com/daniel-hutao) | [Prompts with GPT](./commits/a6315922a026b4290b0b1cd54e95b25e93538047.md) | [给 GPT 的 Prompts](./commits/a6315922a026b4290b0b1cd54e95b25e93538047_zh.md) |

2.4 DevChat 的第二轮回答

了解了,我们需要对获取到的 commit 数据进行一些处理,以满足你的需求。具体来说,我们需要:

  • 将 commit hash 截取为前 7 位。

  • 只保留 commit message 的第一行。

  • 生成 commit 的链接。

  • 生成 commit 的作者的链接。

以下是修改后的 Python 脚本:

import requests
import pandas as pd
from typing import Dict, List

def get_commits(org: str, repo: str) -> List[Dict[str, str]]:
    url = f"https://api.github.com/repos/{org}/{repo}/commits"
    response = requests.get(url)
    response.raise_for_status()
    commits = response.json()
    return [
        {
            "Commit": f"[{commit['commit']['message'].split('\n')[0]}](https://github.com/{org}/{repo}/commit/{commit['sha']})",
            "Commit Hash": commit["sha"][:7],
            "Author": f"[{commit['commit']['author']['name']}](https://github.com/{commit['author']['login']})",
            "Prompts with GPT": f"[Prompts with GPT](./commits/{commit['sha']}.md)",
            "给 GPT 的 Prompts": f"[给 GPT 的 Prompts](./commits/{commit['sha']}_zh.md)",
        }
        for commit in commits
    ]

def append_commits_to_file(commits: List[Dict[str, str]], file_path: str) -> None:
    df = pd.DataFrame(commits)
    with open(file_path, "a") as f:
        f.write(df.to_markdown(index=False))

def main():
    org = "devchat-ai"
    repo = "gopool"
    file_path = "devchat-ai/gopool/index.md"
    commits = get_commits(org, repo)
    append_commits_to_file(commits, file_path)

if __name__ == "__main__":
    main()

这个脚本现在会生成你需要的 Markdown 表格,并将其追加到 index.md 文件中。

很快 DevChat 就给出了回答,不过这次的代码有语法错误:

一开始我就说了,我并不熟悉 Python。所以这里看似一个挺简单的错误,反正我改不来。所以,继续问 GPT-4 咯。

2.5 我再次对 DevChat 说

Escape sequence (backslash) not allowed in expression portion of f-string prior to Python 3.12

2.6 DevChat 回答

对不起,我犯了一个错误。在 f-string 表达式部分,我们不能使用反斜杠作为转义字符。在这种情况下,我们可以先将 commit message 分割成行,然后在 f-string 中使用第一行。以下是修改后的代码:

import requests
import pandas as pd
from typing import Dict, List

def get_commits(org: str, repo: str) -> List[Dict[str, str]]:
    url = f"https://api.github.com/repos/{org}/{repo}/commits"
    response = requests.get(url)
    response.raise_for_status()
    commits = response.json()
    commit_data = []
    for commit in commits:
        commit_message_title = commit['commit']['message'].split('\n', 1)[0]
        commit_data.append({
            "Commit": f"[{commit_message_title}](https://github.com/{org}/{repo}/commit/{commit['sha']})",
            "Commit Hash": commit["sha"][:7],
            "Author": f"[{commit['commit']['author']['name']}](https://github.com/{commit['author']['login']})",
            "Prompts with GPT": f"[Prompts with GPT](./commits/{commit['sha']}.md)",
            "给 GPT 的 Prompts": f"[给 GPT 的 Prompts](./commits/{commit['sha']}_zh.md)",
        })
    return commit_data

def append_commits_to_file(commits: List[Dict[str, str]], file_path: str) -> None:
    df = pd.DataFrame(commits)
    with open(file_path, "a") as f:
        f.write(df.to_markdown(index=False))

def main():
    org = "devchat-ai"
    repo = "gopool"
    file_path = "devchat-ai/gopool/index.md"
    commits = get_commits(org, repo)
    append_commits_to_file(commits, file_path)

if __name__ == "__main__":
    main()

这个脚本现在应该可以正常运行,生成你需要的 Markdown 表格,并将其追加到 index.md 文件中。

我再次运行了下这个代码,终于,表格出来了。

2.7 最后效果

后面我还继续聊了几轮,让 GPT-4 把本地创建表格中出现的 Markdown 文件的逻辑也加上了,所以最后我顺利的通过 Python 脚本生成了下图中的这个巨大无比的表格,以及对应的左侧五十多个 Markdown 文件。

Python 代码最终版本如下:

import os
import requests
import pandas as pd
from typing import Dict, List

def get_commits(org: str, repo: str) -> List[Dict[str, str]]:
    url = f"https://api.github.com/repos/{org}/{repo}/commits"
    response = requests.get(url)
    response.raise_for_status()
    commits = response.json()
    commit_data = []
    for commit in commits:
        commit_message_title = commit['commit']['message'].split('\n', 1)[0]
        commit_data.append({
            "Commit": f"[{commit_message_title}](https://github.com/{org}/{repo}/commit/{commit['sha']})",
            "Commit Hash": commit["sha"],
            "Author": f"[{commit['commit']['author']['name']}](https://github.com/{commit['author']['login']})",
            "Prompts with GPT": f"[Prompts with GPT](./commits/{commit['sha']}.md)",
            "给 GPT 的 Prompts": f"[给 GPT 的 Prompts](./commits/{commit['sha']}_zh.md)",
        })
    return commit_data

def append_commits_to_file(commits: List[Dict[str, str]], file_path: str) -> None:
    df = pd.DataFrame(commits)
    df['Commit Hash'] = df['Commit Hash'].apply(lambda x: x[:7])
    with open(file_path, "a") as f:
        f.write(df.to_markdown(index=False))

def create_commit_files(commit: Dict[str, str], directory: str = "devchat-ai/gopool/commits") -> None:
    """
    Create markdown files for each commit.
    """
    for suffix in ["", "_zh"]:
        file_path = os.path.join(directory, f"{commit['Commit Hash']}{suffix}.md")
        if not os.path.exists(file_path):
            with open(file_path, "w") as f:
                f.write("")

def main():
    org = "devchat-ai"
    repo = "gopool"
    file_path = "devchat-ai/gopool/index.md"
    commits = get_commits(org, repo)
    append_commits_to_file(commits, file_path)
    for commit in commits:
        create_commit_files(commit)

if __name__ == "__main__":
    main()

3. 他们居然让我要写总结

那就总结一下吧。

今天挑战的是让 GPT-4 用我不熟悉的 Python 语言来完成一个自动化任务。

像这种大表格的编辑以及相应的50+个文件的创建,手动操作是很容易出错的,这类活天然适合交给 Python 脚本。不过放在以前,可能这种活我只能选择用自己熟悉的其他高级语言来完成,可以预见 Python 50 行代码对应的 Golang 估计得小100行了。100行啊,一杯水,一包烟,一个脚本写半天。不过现在可以让 GPT-4 来完成这类工作。

几轮“自然语言沟通”就搞定了,算下来可能花了不到30分钟,大约就是:0.5h VS 2.5h 吧。

又是 N 倍效率的一天,马克杯里的咖啡都还没凉,事情又干完了。呼…… 下班,等吃饭!