Chat GPTで勤務シフトは作成できるのか？できないのか？

![勤務シフト用イラスト.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/67847e92-a5ce-e11a-4b7b-274e73dbcfb5.jpeg)

ちまたを騒がしているChatGPT。
論文を書かせてみたり、
様々なアイディアの考察だったり、
問題の解決策の提示だったり、
何でも答えてくれる魔法のようなツールですよね。
これなら煩雑な勤務シフトも作ってくれるのでは！？
と期待を胸に、ChatGPTにシフト作成を依頼してみました！

## ChatGPTに直接シフト作成の依頼をしてみる

### まずは簡単なモデルケースでざっくり依頼してみます

>あなたはスーパーで働く部門リーダーです。
>あなたと部門のメンバーの6月の勤務シフトを作成してください。
>メンバーは4人、リーダーとサブリーダー、アルバイトが2名です。
>リーダーは月曜日と木曜日が休みです。
>リーダーが休みの日はサブリーダーは必ず出勤します。
>火曜日は売上が高いので全員出勤です。
>全員週休2日にしてください。


![ChatGPTに質問1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/03affa4a-7ce8-9464-4214-1a7aa570fd11.png)

ん？
リーダー以外休みがない！？
週休2日って意味が伝わらなかったようです･･･
あと、リーダーは月曜日と木曜日を休みにしているのに
カレンダーとあってないです。
これは2024年って入れないとダメなのかもです。
修正して依頼してみます。

### 休みと曜日の修正依頼

>全員1週間に2回休みをとってください。
>2024年6月1日は土曜日です。
>曜日も表に入れてください。

![ChatGPTに質問2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/d7e28e0e-c3f1-c7c2-ab22-d863ade18d7d.png)


うーん･･･
リーダーの休みはなぜか増えたけど
サブリーダーはまだ週休1日、
アルバイトさんは休みなし･･･
しかも火曜日にリーダーが休みになってしまいました･･･

もう少し詳しく説明しないとダメなのかもです。
修正して依頼してみます。


### 休みを曜日指定にしてもう一度最初から依頼

>あなたはスーパーで働く部門リーダーです。
>あなたと部門のメンバーの2024年6月の勤務シフトを作成してください。
>2024年6月1日は土曜日です。
>メンバーは4人、リーダーとサブリーダー、アルバイトが2名です。
>全員1週間に2回休みをとってください。
>リーダーは月曜日と木曜日が休みです。
>サブリーダーは水曜日と金曜日が休みです。
>火曜日は売上が高いので全員出勤です。
>アルバイト2名は交互に休みをとってください。

![ChatGPTに質問3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/682a43ea-2374-5577-6f75-1e4e31748c66.png)

上手く意図が伝わらないです。
ここまでとは･･･
ChatGPTならこのくらいすぐにできそうと思っていましたが、
意外と難しいですね･･･
毎回正確に依頼するのはすごく大変なのでは？


### ここまでの結論

:::note 
ChatGPTは日付や曜日の認識があいまい？
:::
　　蓄積された過去のデータを元に返事をしてるので
　　未来については回答が適当


:::note
「バランスよく」みたいな質問は正しく回答されない
ChatGPT自身で調整するのは難しい
:::

　　いろいろ試行錯誤するなかで、
　　「1か月で10日間休みにして」などのプロンプトを試してみたのですが、
　　20日間連続出勤して、残り10連休みたいなシフトを作ってきたり･･･
　　私たちが当たり前だと思って自然にやっていたり、
　　考えなくても無意識に導き出してることでも
　　ChatGPTにはいちいち細かく指示をしないと欠如してしまうようです

:::note 
計算ソフトではないので、正確さはない
:::

　　ChatGPTは「言語」ツールなので、
　　正確に処理させるなら、
　　Excelなどの表計算ソフトなどを使った方が圧倒的に正確で早い


ということで、
**ChatGPTによるシフト作成は難しい**
と判断しました。

ですが！
考え方を変えてみます。

ChatGPTそのものにシフトを作ってもらうのは難しいのですが、
シフト作成するプログラムを作ってもらうことはできるかも？

## ChatGPTにシフト作成のプログラミングコードを作ってもらう

プログラミングにすれば毎回正確に実行しますし
エラーならエラーってでてきます。
（ChatGPTさん、できてないのに「できました」とか「修正しました」とか言うんですよね＾＾；）

プログラミング初心者のわたくしでは
まだまだ難しいことは分からないので
まずはGAS（Google Apps Script）[^1]を使ったプログラムを
ChatGPTに依頼してみることにしました。

[^1]:Google Apps Script(グーグル アップス スクリプト)とはGoogleが提供している
アプリケーション開発の為のワークスペースプラットフォーム。 
簡単に言うとプログラムを書く為にGoogleが開発した言語です。

今回はエラーの箇所を明確にするために、
一つずつ段階を踏んで進めていきます。

### まずGoogle スプレッドシートに大枠を入力
※プログラミングの動作テストのため、表の見やすさなどは今回は除外します。

![GAS1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/12b141e4-6709-9909-4b2e-688de8abcac8.png)

日付をC1からAG1に、曜日をC2からAG1に入力するのですが、
これもプログラミングで実行します。
最初から直接入力した方が簡単で早いのですが
ChatGPTに最初から一つずつ順番に作ったもらった方が、
最後まで流れがスムーズでしたので。
また、年と月を変更することで、自動的に日付と曜日も変わるようにします。

### ChatGPTに日付を正しく入力するコードを依頼

>あなたはスーパーで働く部門リーダーです。
>GAS（google Apps Script）で毎月の勤務計画を作りたいです。
>sheetは「勤務計画表」です。
>初心者にもわかるように、次の順に進めてください。
>①まずはカレンダーを作ってください。
>セルA1に「年」が入力されています。
>セルB1に「月」が入力されています。
>1か月が31日の月は、(A1)年(B1)月の日付をC1からAG1に入力するコードを作ってください。
>1か月が30日の月は、(A1)年(B1)月の日付をC1からAF1に入力するコードを作ってください。
>1か月が29日の月は、(A1)年(B1)月の日付をC1からAE1に入力するコードを作ってください。
>1か月が28日の月は、(A1)年(B1)月の日付をC1からAD1に入力するコードを作ってください。

![GAS2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/7176a2dd-625d-c1fc-124e-2d4515f898fb.png)

1か月の日数が違うと、
検索する範囲と出力する範囲が違うというエラーが出てしまうので
31日の月、30日の月などそれぞれ範囲を変更するように指示を出しました。

<details><summary>コードテキスト</summary>

function createCalendar() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // カレンダーを作成
  var calendarRange = "";
  switch (daysInMonth) {
    case 31:
      calendarRange = "C1:AG1";
      break;
    case 30:
      calendarRange = "C1:AF1";
      break;
    case 29:
      calendarRange = "C1:AE1";
      break;
    case 28:
      calendarRange = "C1:AD1";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  // 日付を設定
  var calendarValues = [];
  for (var i = 1; i <= daysInMonth; i++) {
    calendarValues.push([i]);
  }

  // シートに日付を設定
  sheet.getRange(calendarRange).setValues([calendarValues]);
}

</details>

このコードをGASに張り付けて実行すると･･･
![GAS4日付が入力.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/c243bb82-cfca-214c-314f-aafd67a9e636.png)
日付が入力されました！

### 曜日を正しく入力するコードを依頼

>②上記の日付に対応させて曜日を入力するコードを作ってください。
>1か月が31日の月は、(A1)年(B1)月の曜日をC2からAG2に入力するコードを作ってください。
>1か月が30日の月は、(A1)年(B1)月の曜日をC2からAF2に入力するコードを作ってください。
>1か月が29日の月は、(A1)年(B1)月の曜日をC2からAE2に入力するコードを作ってください。
>1か月が28日の月は、(A1)年(B1)月の曜日をC2からAD2に入力するコードを作ってください。

![GAS5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/382a9e88-8b8f-ed34-889d-34eef93e83a2.png)

<details><summary>コードテキスト</summary>

function insertWeekdays() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // 曜日の配列
  var weekdays = ["日", "月", "火", "水", "木", "金", "土"];

  // 曜日を設定
  var calendarRange = "";
  switch (daysInMonth) {
    case 31:
      calendarRange = "C2:AG2";
      break;
    case 30:
      calendarRange = "C2:AF2";
      break;
    case 29:
      calendarRange = "C2:AE2";
      break;
    case 28:
      calendarRange = "C2:AD2";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  // 曜日を計算して設定
  var startDay = new Date(year, month - 1, 1).getDay(); // 月の最初の曜日を取得
  var weekdaysValues = [];
  for (var i = 0; i < daysInMonth; i++) {
    var weekdayIndex = (startDay + i) % 7;
    weekdaysValues.push([weekdays[weekdayIndex]]);
  }

  // シートに曜日を設定
  sheet.getRange(calendarRange).setValues([weekdaysValues]);
}

</details>

このコードをGASに張り付けて実行します

![GAS6曜日が入力.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/8bc59664-9930-1cf4-c6d7-68425d11770d.png)

曜日が正しく入力されました！

### リーダーのシフトを作成する

>③リーダーからシフトを作成します。
>上記の日付と曜日に対応させて、
>1か月が31日の月は、(A1)年(B1)月の曜日をC5からAG5の範囲に入力します。
>1か月が30日の月は、(A1)年(B1)月の曜日をC5からAF5の範囲に入力します。
>1か月が29日の月は、(A1)年(B1)月の曜日をC5からAE5の範囲に入力します。
>1か月が28日の月は、(A1)年(B1)月の曜日をC5からAD5の範囲に入力します。
>リーダーは月曜日と木曜日が休みです。
>出勤日はセル(B5)の数値を、休みの日は「休み」を入力するコードを作ってください。

![GAS7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/8717a1d8-f746-6e1a-2a94-a786169f6a9b.png)

今回は休日を曜日で固定しました。
まだ複雑な変形労働には対応できなかったので、
今後条件をいろいろ増やしてテストしていきたいと思っています。
出勤日には、勤務時間が入力されるようになってます。
勤務時間は行Bの名前の横に入力されています。

<details><summary>コードテキスト</summary>

function createShiftSchedule() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // 曜日の配列
  var weekdays = ["日", "月", "火", "水", "木", "金", "土"];

  // 休みの日と出勤日を設定
  var shiftSchedule = [];
  var startDay = new Date(year, month - 1, 1).getDay(); // 月の最初の曜日を取得
  for (var i = 0; i < daysInMonth; i++) {
    var weekdayIndex = (startDay + i) % 7;
    if (weekdays[weekdayIndex] === "月" || weekdays[weekdayIndex] === "木") {
      shiftSchedule.push(["休み"]);
    } else {
      shiftSchedule.push([sheet.getRange("B5").getValue()]);
    }
  }

  // シートにシフトを設定
  var shiftRange = "";
  switch (daysInMonth) {
    case 31:
      shiftRange = "C5:AG5";
      break;
    case 30:
      shiftRange = "C5:AF5";
      break;
    case 29:
      shiftRange = "C5:AE5";
      break;
    case 28:
      shiftRange = "C5:AD5";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  sheet.getRange(shiftRange).setValues([shiftSchedule]);
}

</details>

このコードをGASに張り付けて実行します。

![GAS8リーダーのシフト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/c34161ea-8d70-10f5-d7f9-9576e4ae0910.png)

こんな感じになります。

### 以下、サブリーダーとアルバイトも依頼

同じ作業の繰り返しなので省略

<details>

>④次に、サブリーダーのシフトを作成します。
>上記の日付と曜日に対応させて、
>1か月が31日の月は、(A1)年(B1)月の曜日をC6からAG6の範囲に入力します。
>1か月が30日の月は、(A1)年(B1)月の曜日をC6からAF6の範囲に入力します。
>1か月が29日の月は、(A1)年(B1)月の曜日をC6からAE6の範囲に入力します。
>1か月が28日の月は、(A1)年(B1)月の曜日をC6からAD6の範囲に入力します。
>サブリーダーは水曜日と金曜日が休みです。
>出勤日はセル(B6)の数値を返してください。、休みの日は「休み」を入力するコードを作ってください。

![GAS9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/92db1d75-c620-4270-aead-3c6e61611d6a.png)

<details><summary>コードテキスト</summary>

function createSubLeaderShiftSchedule() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // 曜日の配列
  var weekdays = ["日", "月", "火", "水", "木", "金", "土"];

  // 休みの日と出勤日を設定
  var shiftSchedule = [];
  var startDay = new Date(year, month - 1, 1).getDay(); // 月の最初の曜日を取得
  for (var i = 0; i < daysInMonth; i++) {
    var weekdayIndex = (startDay + i) % 7;
    if (weekdays[weekdayIndex] === "水" || weekdays[weekdayIndex] === "金") {
      shiftSchedule.push(["休み"]);
    } else {
      shiftSchedule.push([sheet.getRange("B6").getValue()]);
    }
  }

  // シートにシフトを設定
  var shiftRange = "";
  switch (daysInMonth) {
    case 31:
      shiftRange = "C6:AG6";
      break;
    case 30:
      shiftRange = "C6:AF6";
      break;
    case 29:
      shiftRange = "C6:AE6";
      break;
    case 28:
      shiftRange = "C6:AD6";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  sheet.getRange(shiftRange).setValues([shiftSchedule]);
}

</details>

上記のコードを張り付けて実行します。

>⑤アルバイト1のシフトを作成します。
>上記の日付と曜日に対応させて、
>1か月が31日の月は、(A1)年(B1)月の曜日をC7からAG7の範囲に入力します。
>1か月が30日の月は、(A1)年(B1)月の曜日をC7からAF7の範囲に入力します。
>1か月が29日の月は、(A1)年(B1)月の曜日をC7からAE7の範囲に入力します。
>1か月が28日の月は、(A1)年(B1)月の曜日をC7からAD7の範囲に入力します。
>アルバイト1は日曜日と木曜日が休みです。
出勤日はセル(B7)の数値を返してください。、休みの日は「休み」を入力するコードを作ってください。

![GAS10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/3e9349e3-8172-76df-269d-3b1ee135c69d.png)

<details><summary>コードテキスト</summary>

function createPartTime1ShiftSchedule() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // 曜日の配列
  var weekdays = ["日", "月", "火", "水", "木", "金", "土"];

  // 休みの日と出勤日を設定
  var shiftSchedule = [];
  var startDay = new Date(year, month - 1, 1).getDay(); // 月の最初の曜日を取得
  for (var i = 0; i < daysInMonth; i++) {
    var weekdayIndex = (startDay + i) % 7;
    if (weekdays[weekdayIndex] === "日" || weekdays[weekdayIndex] === "木") {
      shiftSchedule.push(["休み"]);
    } else {
      shiftSchedule.push([sheet.getRange("B7").getValue()]);
    }
  }

  // シートにシフトを設定
  var shiftRange = "";
  switch (daysInMonth) {
    case 31:
      shiftRange = "C7:AG7";
      break;
    case 30:
      shiftRange = "C7:AF7";
      break;
    case 29:
      shiftRange = "C7:AE7";
      break;
    case 28:
      shiftRange = "C7:AD7";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  sheet.getRange(shiftRange).setValues([shiftSchedule]);
}

</details>

上記のコードを張り付けて実行します。

>⑥アルバイト2のシフトを作成します。
>上記の日付と曜日に対応させて、
>1か月が31日の月は、(A1)年(B1)月の曜日をC8からAG8の範囲に入力します。
>1か月が30日の月は、(A1)年(B1)月の曜日をC8からAF8の範囲に入力します。
>1か月が29日の月は、(A1)年(B1)月の曜日をC8からAE8の範囲に入力します。
>1か月が28日の月は、(A1)年(B1)月の曜日をC8からAD8の範囲に入力します。
>アルバイト2は月曜と土曜日が休みです。
>出勤日はセル(B8)の数値を返してください。、休みの日は「休み」を入力するコードを作ってください。

![GAS11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/d020ae00-10a1-7fa5-c5ec-dfeccbfb06f6.png)

<details><summary>コードテキスト</summary>

function createPartTime2ShiftSchedule() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("勤務計画表");

  // 年と月を取得
  var year = sheet.getRange("A1").getValue();
  var month = sheet.getRange("B1").getValue();

  // 月の日数を取得
  var daysInMonth = new Date(year, month, 0).getDate();

  // 曜日の配列
  var weekdays = ["日", "月", "火", "水", "木", "金", "土"];

  // 休みの日と出勤日を設定
  var shiftSchedule = [];
  var startDay = new Date(year, month - 1, 1).getDay(); // 月の最初の曜日を取得
  for (var i = 0; i < daysInMonth; i++) {
    var weekdayIndex = (startDay + i) % 7;
    if (weekdays[weekdayIndex] === "月" || weekdays[weekdayIndex] === "土") {
      shiftSchedule.push(["休み"]);
    } else {
      shiftSchedule.push([sheet.getRange("B8").getValue()]);
    }
  }

  // シートにシフトを設定
  var shiftRange = "";
  switch (daysInMonth) {
    case 31:
      shiftRange = "C8:AG8";
      break;
    case 30:
      shiftRange = "C8:AF8";
      break;
    case 29:
      shiftRange = "C8:AE8";
      break;
    case 28:
      shiftRange = "C8:AD8";
      break;
    default:
      Logger.log("Invalid number of days in the month");
      return;
  }

  sheet.getRange(shiftRange).setValues([shiftSchedule]);
}

</details>


上記のコードを張り付けて実行します。

</details>

![GAS12シフト3人まとめて.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763781/c08840ef-9124-1b2f-8c49-89869606d84a.png)
残り3人分のシフトも完成しました！



## 今後の課題、次の挑戦

今回は最初ということもあって、
曜日による完全固定のシフトしか作成できませんでした。
これだと、特にプログラミングに頼らなくても
Excelで簡単に作れます。

次は、
**①お休みの希望に対応するなど変形労働にするには？**
**②特売日などに人を厚くするにはどうすればいいか？**
**③時間帯でコントロールするにはどうすればいいか？**
などができると、レジのスケジュールもできるようになると考えています。
すごく難しい･･･

## まとめ

シフトの作成をプログラミングすることは簡単ではないんだなと思い知らされました。
わたくしたちが普段手作業でつくってるシフトは、
人の知識と経験でうまくバランス調整して作られたものであって、
それを一つ一つプログラミング言語にしていくためには
まだまだいろいろなことを勉強しないといけないようです。

人は歩くときに
右足を出したら、自然と左手が前に出ます。
これは無意識に重心のバランスをとっているからであって
だれでも当たり前にできることです。

でも、プログラミングの世界では
歩くために右足を出すように指示しても、左手は動きません。
ちゃんと「左手を前に出す」って指示をしないと、転んじゃうんですね。
こういった「隠された指示」をどうやって見つけて
正確に表現していくかが、この世界の難しさなのかなと感じました。