2026.02.03

「月間シフト表」を自動作成するアプリの作成(Dify)

現場のシフト作成って、地味に時間が溶けます。
「希望は通したい」「でも上限回数は守らないといけない」「曜日や時間帯も人によって違う」——この調整を毎月手作業でやるのは、正直つらい。

そこで今回は、Difyのチャットフロー + Codeノード(Python)を使って、月間シフト表を自動生成するアプリを制作しました。
個別条件の多い現場でも回せるように、LLMだけに頼らず、
“制約はコードで守る”設計にしています。


どんなシフトを作るアプリ?

シフトの前提

  • 1か月単位で作成(例:2026-03)
  • 1日の枠は short / long / stay(泊まり) の3種類
  • 日ごとに「日タイプ」があり、タイプによって必要枠が変わる
    • weekday(平日):short / long / stay
    • sunday(日曜):short / long / stay
    • holiday(祝日):short / long / stay
    • saturday_p1(土曜P1):short / stay
    • saturday_p2(土曜P2):short / long / stay

守るべき制約(ハード)

  • 月上限:最大4回(個別上限があればそちら優先)
  • 週上限:1人1回まで(week_id基準)
  • 同日同職員の重複なし
  • 「入れない(日付 / 曜日 / 日タイプ / stayのみ)」を厳守
  • 「許可枠(allow_slots)」と「許可日タイプ(allow_types)」を厳守
    (例:平日shortのみ、日祝longのみ…など)

希望の扱い(ソフトとハード)

  • 希望は2段階にしました
    • 希望(hard):できれば必ず入れたい(ただし固定枠や制約で不可能な場合は未達として出す)
    • 希望(soft):入れられたら嬉しい

なぜLLMだけだと崩れるのか?

最初は「LLMに全部割り振らせる」方針も検討しましたが、実運用では以下が起きがちでした。

  • 月上限4回が守られない
  • 逆に怖がって全員0回みたいな結果が出る
  • hard希望が入っているのに割り当てられない
  • 途中の手順を説明する文章が長くなり、出力が読みづらい

つまり、LLMは“方針”は強いけど、厳密な回数制限や探索(入れ替え調整)が苦手です。
そこで、LLM=カレンダー生成 / コード=割当ロジックで役割分担する形に切り替えました。


仕組み:Difyチャットフローでの構成

今回のフローは大きく2段階です。

① LLM①:対象月のカレンダー(枠一覧)を作る

ユーザーが入力した対象月と「日別区分(weekday / holiday…)」をもとに、

  • date(YYYY-MM-DD)
  • slot(short/long/stay)
  • day_type
  • weekday(mon..sun)
  • week_id

をTSVで出力します。

この段階では「割当」はしません。
**“候補となる枠一覧(カレンダー)を整形して作る”**のが役目です。


② Codeノード:固定枠の除外 → 割当 → 仕上げ調整(swap)

次に、Codeノード(Python)が以下を行います。

  1. 固定枠TSVの(date,slot)を候補から除外
     (固定枠は既に確定している前提)
  2. 希望(hard)を最優先で割当
     ただし上限・週上限・禁止条件に反する場合は未達として記録
  3. 希望(soft)を割当
  4. 残り枠を公平に割当(0回救済を含む)
  5. それでも空欄が残る場合
     swap(入れ替え)探索で空欄を減らす
     → “空欄を直接埋める”のではなく、
      埋められる人を別枠から解放して埋める、という調整

入力はTSVで統一(現場運用しやすい)

入力はすべてTSVにしています。
Excelで管理している現場が多いので、TSVにすると運用が簡単です。

  • 職員TSV(希望・NG・上限・許可枠など)
  • 固定枠TSV(候補から除外する枠)
  • カレンダーTSV(LLM①の出力)

出力は「TSVと回数だけ」に絞る

運用で欲しいのは結局ここだけです。

  • シフト案TSV
    日付 / 枠 / 担当者
  • 職員ごとの割当回数(0回も含める)
  • hard未達がある場合だけ、そのリスト(理由付き)

長い説明文は出さず、現場で使いやすい出力に寄せています。


使ってみて良かった点

  • 制約(回数・週上限)が安定して守れる
  • hard希望の扱いが明確(無理な場合は未達として出る)
  • 0回が大量発生しにくく、分配が改善
  • 固定枠を「候補から外す」運用が可能で、現場ルールに合わせやすい

今後の改善予定

現場運用を想定すると、次の改善が効きそうです。

  • **枠別の上限(例:shortは月2回まで)**を列として追加
  • hard希望未達の理由をもう少し具体化
    (固定枠で消えている / allow_slotsで不可 / 週上限で不可 など)
  • 出力をそのまま貼れるように
    **“日付×枠の表形式”**にも対応(必要なら)

まとめ:AI+ルールで「現実的に使える自動シフト」へ

今回のポイントは、「LLMに全部任せない」ことでした。

  • LLM:カレンダー整形、入力の解釈
  • コード:制約を厳密に守る割当、入れ替え調整

この役割分担にすることで、精度と再現性が一気に上がりました

もし同じように、
「毎月の調整が大変」「人別の制約が多い」「手作業が限界」
という業務があれば、Dify+コードの組み合わせはかなり相性が良いと思います。

上部へスクロール