Bard使ってみた

# はじめに
[BardについてのFAQ](https://bard.google.com/faq)
[Apps Script](https://workspace.google.co.jp/intl/ja/products/apps-script/)

細かい準備は割愛してある程度対話型AIの使い方を知っていることを前提として、与える情報によるアウトプットの違い、それに期待している出力に近づけるためにできる改善案を書いていこうと思います。

# 背景
これまでプログラム書きたいとか勉強したいという声をたくさん聞いてきました。
最近では対話型AIの登場によりお手軽にソースコードが出力できるようになったという話を聞き、実際にどれくらいお手軽にソースコードが出力できるのかと、実際に世の中にあるサービスへ組み込めるような内容なのかという所に興味が湧いたため実際に使ってみました。

# やりたいこと
* 期待する結果に近い形でコードが出力されるためにプロンプトへ与える情報を変化させながら出力をみる

# さっそく適当にコードを出力
```javascript:プロンプト
時刻を指定するとある期間を開催前、開催中、開催後のどれに該当するか表示するコードを出力
```

```python:結果
はい、わかりました。次のコードは、時刻を指定すると、期間を開催前、開催中、開催後のどれに該当するかを表示します。

def get_event_status(time):
  """
  時刻を指定して、イベントのステータスを返す。

  Args:
  time: 時刻。

  Returns:
  イベントのステータス。
  """

  #イベントの開始時刻と終了時刻を取得する。
  start_time = datetime.datetime(2023, 6, 28, 10, 0, 0)
  end_time = datetime.datetime(2023, 6, 28, 18, 0, 0)

  #指定された時刻がイベントの開始時刻より前かどうかをチェックする。
  if time < start_time:
    return "開催前"

  #指定された時刻がイベントの終了時刻より後かどうかをチェックする。
  if time > end_time:
    return "開催後"

  #指定された時刻がイベントの開始時刻から終了時刻の間にある場合、イベントのステータスを「開催中」と返す。
  return "開催中"

  if name == "main":

  #時刻を入力する。
  time = input("時刻を入力してください: ")

  #イベントのステータスを取得する。
  event_status = get_event_status(datetime.datetime.strptime(time, "%Y-%m-%d %H:%M:%S"))

  #イベントのステータスを表示する。
  print(event_status)


このコードを実行すると、次のようになります。

```

多分Pythonなんですが、わたしが間違えて説明しそうなのでApps Scriptでの出力だけ指定します。

## 言語だけ指定
```javascript:プロンプト
時刻を指定するとある期間を開催前、開催中、開催後のどれに該当するか表示するコードをApps Scriptで出力
```
```javascript: 結果
function checkEventPeriod(eventStart, eventEnd, currentTime) {
  // イベント開始日時と終了日時を比較します。
  if (eventStart < currentTime && currentTime < eventEnd) {
    // イベント開催中です。
    return "開催中";
  } else if (eventStart > currentTime) {
    // イベント開催前です。
    return "開催前";
  } else {
    // イベント開催後です。
    return "開催後";
  }
}
```
なんとなく動きそうなコードが出力されましたが、多分プログラムについての知識が無い人がこのコードをなんとか実行可能な状態まで持っていったとしても色々と罠がありそうですね。
次はプログラムに関してドメイン知識を持っていることを前提にプロンプトへ渡す情報を変更してみます。

# 要件をリストで記述してプロンプトに与える
```javascript:プロンプト
対象とする言語: Apps Script
フレームワークは使用しない
最初に呼ばれる関数名: main
時刻を与えると開催前、開催中、開催後の3つのステータスから該当するステータスを返す関数checkPeriodのコードを出力
checkPeriodは3つの引数を持ち、それぞれ、時刻、期間の開始時刻、期間の終了時刻とする
関数checkPeriodはmain関数から呼び出される
期間は 2023/06/01 00:00:00 から 2023/06/15 23:59:59
```
```javascript:結果
function checkPeriod(time, startTime, endTime) {
  // 入力時刻が期間内かどうかをチェックする
  if (time >= startTime && time <= endTime) {
    // 期間内の場合、開催中を返す
    return "開催中";
  } else {
    // 期間外の場合、開催前または開催後を返す
    return (time < startTime) ? "開催前" : "開催後";
  }
}

function main() {
  // 現在の時刻を取得する
  var now = new Date();

  // 開始時刻と終了時刻を設定
  var startTime = new Date("2023-06-01T00:00:00");
  var endTime = new Date("2023-06-15T23:59:59");

  // 関数checkPeriodを呼び出し、その結果を表示する
  var status = checkPeriod(now, startTime, endTime);

  // ステータスをログに出力する
  Logger.log(status);
}
```
これくらいのコードが出力されると、社内用のツール類では全く困らずに使用できるんじゃ無いかと思います。

ついでにユニットテストの出力もやってみます。

```javascript:プロンプト
対象とする言語: Apps Script
フレームワークは使用しない
最初に呼ばれる関数名: main
時刻を与えると開催前、開催中、開催後の3つのステータスから該当するステータスを返す関数checkPeriodのコードを出力
checkPeriodは3つの引数を持ち、それぞれ、時刻、期間の開始時刻、期間の終了時刻とする
関数checkPeriodはmain関数から呼び出される
期間は 2023/06/01 00:00:00 から 2023/06/15 23:59:59
checkPeriod関数のユニットテストも出力
ユニットテストはtestCheckPeriod関数を呼び出すことで実行
結果は実行ログへ出力
```
```javascript:結果
function checkPeriod(time, startDate, endDate) {
  // 時刻が期間内かどうかをチェックする
  if (time >= startDate && time <= endDate) {
    // 期間内
    return "開催中";
  } else if (time < startDate) {
    // 期間前
    return "開催前";
  } else {
    // 期間後
    return "開催後";
  }
}

function testCheckPeriod() {
  // 期間内の場合のテスト
  var time = new Date("2023-06-05T12:00:00");
  var startDate = new Date("2023-06-01T00:00:00");
  var endDate = new Date("2023-06-15T23:59:59");
  var expectedStatus = "開催中";
  var actualStatus = checkPeriod(time, startDate, endDate);
  if (actualStatus !== expectedStatus) {
    throw new Error("Assertion failed");
  }

  // 期間前の場合のテスト
  time = new Date("2023-05-31T23:59:59");
  expectedStatus = "開催前";
  actualStatus = checkPeriod(time, startDate, endDate);
  if (actualStatus !== expectedStatus) {
    throw new Error("Assertion failed");
  }

  // 期間後の場合のテスト
  time = new Date("2023-06-16T00:00:00");
  expectedStatus = "開催後";
  actualStatus = checkPeriod(time, startDate, endDate);
  if (actualStatus !== expectedStatus) {
    throw new Error("Assertion failed");
  }
}

function main() {
  // 時刻を取得する
  var time = new Date();

  // 期間の開始時刻と終了時刻を取得する
  var startDate = new Date("2023-06-01T00:00:00");
  var endDate = new Date("2023-06-15T23:59:59");

  // 期間のステータスを取得する
  var status = checkPeriod(time, startDate, endDate);

  // ステータスをログに出力する
  Logger.log(status);
}
```
checkPeriod関数の実装が変化しているんですが・・・。
この辺りはどのコードを使用して何をしたいのかを明確にしたほうが良いですね。

出力されたコードだと似たような処理が多いのとテストデータとテストコードが分割できていないのがちょっと嫌です。
そして一連のテストでどこかで失敗した時にthrowされるため、後続するテストが行われないところももう少し使いやすくして欲しい。
この辺りまでコード出力されたらあとはリファクタでもなんとかやれますが、今回はBardを使用してどこまでできるか試してみます。

```javascript:プロンプト
対象とする言語: Apps Script
フレームワークは使用しない
時刻を与えると開催前、開催中、開催後の3つのステータスから該当するステータスを返す関数checkPeriodのコードを出力
checkPeriodは3つの引数を持ち、それぞれ、時刻、期間の開始時刻、期間の終了時刻とする
関数checkPeriodはmain関数から呼び出される
期間は 2023/06/01 00:00:00 から 2023/06/15 23:59:59

checkPeriod関数のユニットテストも出力
ユニットテストはtestCheckPeriod関数を呼び出すことで実行
テストデータは連想配列、または配列を使用して定義
テストデータは時刻、開始時刻、終了時刻、期待結果で構成する
テスト結果は、テストデータとあわせて合否を実行ログへ出力
```

```javascript:結果
function checkPeriod(time, start, end) {
  if (time < start) {
    return "開催前";
  } else if (time <= end) {
    return "開催中";
  } else {
    return "開催後";
  }
}

function testCheckPeriod() {
  var testData = [
    {
      time: "2023-05-31 23:59:59",
      start: "2023-06-01 00:00:00",
      end: "2023-06-15 23:59:59",
      expectedResult: "開催前",
    },
    {
      time: "2023-06-01 00:00:00",
      start: "2023-06-01 00:00:00",
      end: "2023-06-15 23:59:59",
      expectedResult: "開催中",
    },
    {
      time: "2023-06-15 23:59:59",
      start: "2023-06-01 00:00:00",
      end: "2023-06-15 23:59:59",
      expectedResult: "開催中",
    },
    {
      time: "2023-06-16 00:00:00",
      start: "2023-06-01 00:00:00",
      end: "2023-06-15 23:59:59",
      expectedResult: "開催後",
    },
  ];

  for (var i = 0; i < testData.length; i++) {
    var result = checkPeriod(testData[i].time, testData[i].start, testData[i].end);
    if (result !== testData[i].expectedResult) {
      console.log("テスト失敗: " + testData[i].time + "は" + result + "とすべきですが、" + testData[i].expectedResult + "です。");
    }
  }
}
```
成功は出力されなかったんですが失敗は出力されるようになり、throwしないコードになったので最後まで実行されます。
またfor文で実装されていますが、この辺りは先回りしてfor分はforEachでとプロンプトに指定することで回避が可能です。
テストコードそのものへの例外は考慮されていないためその辺りも考えるべきなのですが、Bardでどれだけ出力できるかを見たかったということで今回は除外。

# まとめ
今回出力したようなコードはネットで調べても当たり前のようにあふれているので正直驚くことはなかった、というか当然のように出力されたのでもうちょっと驚いてもいい内容なのかもしれない。

自然言語で記述した内容でコードを出力しようとすると、ちょいちょいいい感じの内容で指定した通りの抽象度で出力されてくるのでそういう用途で使用する人はそれでも使えると思う。
(使い捨ての何かの説明に使用するようなコード)

しかしながらドメイン知識を使用して記述すること(記述できること）と、要件(や設計)を詳細に与えることで出力についてもある程度コントロールが可能なことがわかった。
この点については詳細なコードの書き方がわからなくても書籍やネットで身につけながらプロンプトへの情報を与えられるようになる。

逆説的に、構造化されたアプリケーションの一部のコードを出力する用途でAIを活用する場合、前提となる言語、入力値、出力値、要件、設計、場合によってはフレームワークなどをプロンプトに与える必要があるのだけども、実際にプロンプトに入力可能な文字数に制限があって現状はそのまま使用することは難しいことがある。

対話型AIの活用にあたり、プロンプトへの暗黙的な事前情報が与えられるようになり、それをテンプレートやプロジェクトファイルとして扱えるようになると開発の現場でも使いやすくなるんじゃ無いのかな〜という未来を感じました。

無償でここまでできるってすごい。