ソースコードをChat GPTに書いてもらうために、最適なプロンプト文を考える

2023年5月8日月曜日

Chat GPT

t f B! P L

Chat GPTを使ってプログラム書いていますか?

プログラマにとって、仕事が奪われる脅威とも言える存在ですが、今のところ有識者なしに完全なコードを書くのは難しいといえる状況でしょう。(これ以上、モデルが賢くなったらヤバいかも)まぁ、それはさておき、Chat GPTをうまく活用することで、普段のコーディング速度を比較的にアップさせることが可能です。

スポンサーリンク

Chat GPTがプログラムを書くのに必要なインプット

Chat GPTは、あなたの心が読めるわけではありません。作って欲しいプログラムに必要なインプットとアウトプットを正確に伝える必要があります。

まずは「Chat GPTのことは、Chat GPTに聞け」と言うことで、Chat GPTがプログラムのソースコード書くために必要なインプットについて尋ねてみた。

<問>

プログラムのソースコードをchatgptに書いてもらうために、最適なプロンプト文の構文を教えて

<答>

プログラムのソースコードをChatGPTに書いてもらうために、最適なプロンプト文の構文は、以下の要素を含めることが重要です:

プログラムの目的:プログラムが何をするかを明確に説明してください。
使用するプログラミング言語:Python、JavaScript、Javaなど、どの言語でコードを書いてほしいか指定してください。
入力と出力:プログラムが受け取るべき入力と期待される出力を説明してください。
必要な機能や制約:特定のアルゴリズム、データ構造、制約条件や入力形式などがあれば明記してください。
例:

"Pythonで、与えられた整数リストの要素の合計を計算するプログラムを書いてください。入力は整数のリストで、出力はそのリストの要素の合計です。"

ある意味、当然の回答が返ってきた。つまり、人間にプログラム仕様を伝える時と同じ要領なのかもしれない。

とにかく作ってみる

実際にChat GPTを使って、簡単なTodoアプリを作ってみます。
今回は、フロントエンド・バックエンド・DBの構成は、次のようにします。

フロントエンド:React
バックエンド:Python(フレームワークはFast API)
DB:SQLite

プロンプト文について考える

Chat GPTは話し言葉でもいい感じのプログラムのコードを書いてくれます。ただ、細かい条件を指定するときや、長めの仕様を使えるときは、構造化されたプロンプトを使用した方が良い結果が返ってきます。
また、1つのプロンプトで全部の機能を作ることは避けた方がよいです。例えば「PythonでTodoアプリを作って」なんて質問をすると、意図しない結果や、必要ない機能まで作られます。基本は人間に仕様を伝えるのと同じ要領で、Todoアプリに必要なデータ構造や機能を細分化して検討し、必要な機能ごとにプロンプトを作成してコードを書いてもらいます。

必要な機能を考える

Todoアプリに必要な機能を考えます。今回は簡単な機能だけに絞ります。
そして、必要な機能の案だしもChat GPTにやってらもいます。

enter image description here

今回は、この中から「タスクの作成」「タスクの編集」「タスクの削除」「タスクの完了状態の更新」「タスクの一覧表示」の機能を実装したいと思います。

データベースの構造を考える

まず、Chat GPTにTodoアプリのデータ構造を覚えてもらいます。データ構造はMarkdownの表形式で伝えると理解してもらいやすいです。

これから、Todoアプリを作成します。Todoアプリのデータ構造は以下のとおりです。このデータ構造を記憶しておいてください。

## データ構造
論理名 | データ型
-------- | -------------
タスクID | Integer型
タスク名 | Text型
完了状態 | Boolean型

enter image description here

使用するデータベースは手軽な「SQLite」を使います。ついでに、SQLiteにテーブルを作成するための CREATE TABEL もChat GPTに作ってもらいます。

このデータ構造のSQLite用のCreate Table文を生成して

enter image description here

バックエンドの実装をする

バックエンドのREST APIを、PythonのFast APIで作ります。次のように具体的に言語、フレームワーク、DB、APIの種類を指定したプロンプトを投げます。

以下の条件で、REST APIのコードを書いて

## 言語
- Python
## フレームワーク
- Fast API
## データベース
- sqlalchemy(SQLite)
## REST APIの種類
- タスクの一覧を取得
- タスクIDを指定してタスクを1件取得
- タスクの登録
- タスクの更新
- タスクの削除
- タスクの完了状態の更新

こんな感じでコードが生成されました。
enter image description here

生成されたコード長いので全部は載せていませんが、指示したとおり、Todoの登録・更新・削除・完了状態の更新をする4つの関数(API)が作られました。また、SQLiteのアクセスにSQLAlchemyを使ってくれています。以下が、上で投げたプロンプトで作成された関数の一覧です。

関数名 HTTPメソッド URL
get_tasks GET /tasks
get_task GET
create_task POST /tasks
update_task PUT /tasks/{task_id}
delete_task DELETE /tasks/{task_id}
update_task_status PATCH /tasks/{task_id}

この後のフロントエンドの実装で、この表のREST APIを呼び出します。

いい感じのコードを生成してくれたので、ちょっとChat GPTを褒めてみましょう(笑)
enter image description here

スポンサーリンク

フロントエンドを実装する

フロントエンド側の実装をします。フロントエンドは「React」を使って実装し、UIライブラリには「MUI」を使用します。作成する機能は次のとおりです。(まぁ、一般的なTodoアプリには必ずある機能ですね)

  • タスクの一覧表示
  • タスクの新規登録(ダイアログ画面)
  • タスクの更新・削除(ダイアログ画面)
  • 完了状態の更新

まず最初に、フロントエンドで作成する機能の概要、使用するライブラリなどをChat GPTに伝えます。この作業によって、この後に投げるプロンプトは、ここで指定した内容を前提条件にして回答を返してくれます。

次は、ここまでに作成したREST APIを使用して、フロントエンドの実装をしたいと思います。

## フロントエンドに必要な機能(コンポーネント)

- タスク一覧: タスクの一覧表示、完了状態の更新を行う
- タスク編集: タスク新規登録・更新・削除を行う

## 使用するフレームワーク
Reactを使います。
また、UIライブラリにはMUIを使います

## 画面遷移
最初に「タスク一覧」の画面が表示されます。
「タスク編集」の画面は、「タスク一覧」の画面からダイアログとして表示されます。

詳細な仕様は後から伝えます。まずは、この内容が理解できたら「はい」と答えてください。

プロンプトの最後に上記の1文を入れるのがポイントです。こうすることで、Chat GPTは、この時点のプロンプトでは「はい」とのみ答え、コードの生成しません。

タスク編集の実装

最初に、タスクの登録・更新・削除をするタスク編集画面を作ります。UI側は意図した動きになるように、詳細に指示をします。

まず、タスク編集の画面のコードを作成してください。
この画面は一覧画面からタスクを編集する場合と、新規登録の時に呼び出されます。

## 引数
タスクを編集する場合は、引数に対象のタスクが渡されます。
新規登録の場合は、引数はnullです。

## 動作仕様

### 初期表示
引数のタスク情報を基に入力項目に値を設定します。

### 登録ボタン
タスクを編集する場合は、REST APIの「update_task」を呼び出します。
タスクを登録する場合は、REST APIの「create_task」を呼び出します。

### 削除ボタン
削除ボタンはタスクを編集する場合のみ表示します。
ボタンがクリックされると、REST APIの「delete_task」を呼び出します。

### 削除ボタン

## 使用するREST API
| 関数名 | HTTPメソッド | URL |
|--|--|--|
| create_task | POST | /tasks |
| update_task | PUT | /tasks/{task_id} |
| delete_task | DELETE | /tasks/{task_id} |


上のプロンプトを実行すると、TaskEditor というコンポーネントを生成してくれました。

enter image description here

ちょっと、細かい内容のプロンプトなので書くのに少し苦労しますが、ここまで指定できると、Chat GPTは、ほぼ手直し不要なコードを書きてくれます。

以下が、作成された TaskEditor コンポーネントのコード全文です。

特にプロンプトで指示はしていませんが、タスク名を入力するまで登録(更新)ボタンを無効状態にする粋な計らいまでしてくれています。。。

import React, { useState, useEffect } from "react";
import { makeStyles } from "@mui/styles";
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  TextField,
  Button,
} from "@mui/material";
import axios from "axios";

const useStyles = makeStyles({
  inputField: {
    marginBottom: "16px",
  },
});

const TaskEditor = ({ task, onClose }) => {
  const classes = useStyles();
  const [taskName, setTaskName] = useState("");
  const [isCompleted, setIsCompleted] = useState(false);
  const [isEditing, setIsEditing] = useState(false);

  // 初期表示時に引数のタスク情報を入力項目に設定する
  useEffect(() => {
    if (task) {
      setTaskName(task.task_name);
      setIsCompleted(task.is_completed);
      setIsEditing(true);
    }
  }, [task]);

  // タスクの登録・更新処理
  const handleSave = async () => {
    try {
      if (isEditing) {
        // タスクを編集する場合
        await axios.put(`/tasks/${task.task_id}`, {
          task_name: taskName,
          is_completed: isCompleted,
        });
      } else {
        // タスクを新規登録する場合
        await axios.post(`/tasks`, {
          task_name: taskName,
        });
      }
      onClose(true);
    } catch (err) {
      console.error(err);
    }
  };

  // タスクの削除処理
  const handleDelete = async () => {
    try {
      await axios.delete(`/tasks/${task.task_id}`);
      onClose(true);
    } catch (err) {
      console.error(err);
    }
  };

  return (
    <Dialog open={true} onClose={() => onClose(false)}>
      <DialogTitle>{isEditing ? "タスクを編集" : "新規タスクの作成"}</DialogTitle>
      <DialogContent>
        <TextField
          className={classes.inputField}
          label="タスク名"
          fullWidth
          value={taskName}
          onChange={(e) => setTaskName(e.target.value)}
        />
        <div className={classes.inputField}>
          <Button
            variant={isCompleted ? "contained" : "outlined"}
            onClick={() => setIsCompleted(true)}
          >
            完了
          </Button>
          <Button
            variant={!isCompleted ? "contained" : "outlined"}
            onClick={() => setIsCompleted(false)}
          >
            未完了
          </Button>
        </div>
      </DialogContent>
      <DialogActions>
        {isEditing && (
          <Button color="error" onClick={handleDelete}>
            削除
          </Button>
        )}
        <Button onClick={() => onClose(false)}>キャンセル</Button>
        <Button onClick={handleSave} disabled={!taskName}>
          {isEditing ? "更新" : "登録"}
        </Button>
      </DialogActions>
    </Dialog>
  );
};

export default TaskEditor;

一覧画面の実装

次にタスク一覧の画面を作ります。タスク編集画面と同じ要領でプロンプトを作成します。

次にタスク一覧の画面のコードを作成してください。


## 引数
なし

## 動作仕様

### 初期表示
REST APIの「get_tasks」を使用し、タスクの一覧を表示する。
タスクの一覧には、次の項目を表示する。
- タスク名
- 完了状態を変更するチェックボックス
- 編集ボタン

### 完了状態のチェックボックス変更時
REST APIの「update_task_status」を使用し、タスクの完了状態を更新する。

### 編集ボタン
タスク一覧で選択した対象の行のタスクを引数に、タスク編集ダイアログを引数なしで表示します。

### 新規登録
タスク編集ダイアログを引数なしで表示します。

上のプロンプトを流すと TaskList というコンポーネントが生成されました。

enter image description here

以下が、作成されたコードの全文です。

ちゃんと、起動時にPythonで作ったAPIを呼び出しデータを取得しており、編集ボタン・新規登録ボタン、完了状態を変更するためチェックボックスについても、ほぼ完璧に処理が実装されています。

import React, { useState, useEffect } from "react";
import { makeStyles } from "@mui/styles";
import {
  Paper,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  Checkbox,
  Button,
} from "@mui/material";
import axios from "axios";
import TaskEditor from "./TaskEditor";

const useStyles = makeStyles({
  root: {
    maxWidth: "800px",
    margin: "0 auto",
  },
});

const TaskList = () => {
  const classes = useStyles();
  const [tasks, setTasks] = useState([]);
  const [isEditing, setIsEditing] = useState(false);
  const [editingTask, setEditingTask] = useState(null);

  // タスク一覧を取得する
  const fetchTasks = async () => {
    try {
      const response = await axios.get(`/tasks`);
      setTasks(response.data);
    } catch (err) {
      console.error(err);
    }
  };

  useEffect(() => {
    fetchTasks();
  }, []);

  // 完了状態を更新する
  const handleToggleComplete = async (task) => {
    try {
      await axios.put(`/tasks/${task.task_id}/complete`);
      fetchTasks();
    } catch (err) {
      console.error(err);
    }
  };

  // タスク編集ダイアログを開く
  const handleEdit = (task) => {
    setEditingTask(task);
    setIsEditing(true);
  };

  // タスク編集ダイアログを閉じる
  const handleCloseEditDialog = (isSaved) => {
    if (isSaved) {
      fetchTasks();
    }
    setIsEditing(false);
    setEditingTask(null);
  };

  return (
    <div className={classes.root}>
      <h1>タスク一覧</h1>
      <TableContainer component={Paper}>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell>タスク名</TableCell>
              <TableCell>完了</TableCell>
              <TableCell></TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {tasks.map((task) => (
              <TableRow key={task.task_id}>
                <TableCell>{task.task_name}</TableCell>
                <TableCell>
                  <Checkbox
                    checked={task.is_completed}
                    onChange={() => handleToggleComplete(task)}
                  />
                </TableCell>
                <TableCell>
                  <Button onClick={() => handleEdit(task)}>編集</Button>
                </TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </TableContainer>
      <Button onClick={() => setIsEditing(true)}>新規登録</Button>
      {isEditing && (
        <TaskEditor task={editingTask} onClose={handleCloseEditDialog} />
      )}
    </div>
  );
};

export default TaskList;

まとめ

Chat GPTを使って、簡単なTodoアプリを作成する方法を見てきました。

すでにお気づきの方もいると思いますが、ソースコードを生成してもらうためのプロンプトは、これまで私たちエンジニアが作成してきたプログラムの仕様書に近い面があります。Chat GPTはMarkdownを構造化された文書として読み取ってくれるため、システム開発の仕様書をMarkdown形式で書いておくと、その大半のソースコードをChat GPTで生成できるかもしれません。

巷では、ノーコード、ローコードツールだと言われますが、もしかすると、Chat GPTが生成したソースコードのレビューができるエンジニア+プロンプトエンジニアが最強になる時代がくるかもしれません。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ