AIで会議室予約の重複を確認するときは、時刻が近い予定をまとめるだけでなく、同じ会議室か、時間が実際に重なるか、取消や仮予約をどう扱うかを決めます。会議終了と同時に次の予約を入れられるかも、片付け時間の条件で変わります。
この記事では、架空の予約6件から競合候補を作り、終了後10分を確保する場合と比べます。成果物は、予約を変更する案ではなく、確認する予約ペアと根拠です。実カレンダーの取得、AI分析、予約変更、会議室の確保は行っていません。
目次
同じ日・会議室・状態の記録をそろえる
例は2026年11月4日、日本時間です。会議室A・Bは別の設備で、同じ名前の会議であっても部屋が違えば、今回の会議室競合ではありません。参加者の重複や移動時間は別の確認です。
表は横にスクロールできます
| 予約ID | 会議室 | 開始 | 終了 | 状態 |
|---|---|---|---|---|
| R1 | A | 09:00 | 10:00 | 確定 |
| R2 | A | 10:00 | 11:00 | 確定 |
| R3 | A | 09:45 | 10:15 | 仮予約 |
| R4 | B | 09:45 | 10:15 | 確定 |
| R5 | A | 11:00 | 11:30 | 取消 |
| R6 | A | 11:00 | 12:00 | 確定 |
すべて独自の架空データです。今回の運用条件は「確定と仮予約は利用枠を押さえる。取消は競合判定から外す」です。仮予約を確定へ変えることではなく、競合を調べる対象に含める設定です。Google Calendarなどの既定動作を表したものではありません。
予約ID、設備ID、日付、開始・終了、状態、取得時点を残します。取消行を元表から削除したり、仮予約を確定と書き換えたりせず、判定へ含めた理由を追えるようにします。
終了と開始が同時なら、重複しない条件にする
今回の時間区間は、開始を含み、終了を含まない形で扱います。09:00~10:00と10:00~11:00は、10時を境に接するだけなので、時間の重なりは0分です。
Google Calendar APIのFreebusy資料も、busy区間の開始を含み、終了を含まないと定義しています。ここではその区間表現を参考にしますが、実際にAPIを呼び出した例ではありません。Google Calendar:Freebusy query(新しいタブで開く)
二つの予約の重なりは、「遅い方の開始」より「早い方の終了」が後にある場合に生じます。同時刻なら0分です。開始・終了が逆の記録や、タイムゾーンが不明な記録は、そのまま比較しないでください。
片付け時間なしでは、競合するペアは二つ
表は横にスクロールできます
| 予約ペア | 共通の会議室 | 重なる区間 | 重なり |
|---|---|---|---|
| R1・R3 | A | 09:45~10:00 | 15分 |
| R2・R3 | A | 10:00~10:15 | 15分 |
R1・R2は終了と開始が接するため、この条件では競合しません。R2・R6も11時で接するため同様です。R4は同じ時間帯でも会議室B、R5は取消なので、今回の競合ペアへ含めません。
この結果から、R3を取り消す、別室へ移すといった結論は自動で決めません。仮予約にも調整中の理由があるかもしれず、どの予約を優先するかは入力されていません。競合を発見したことと、調整の合意ができたことを分けます。
終了後10分を確保すると、隣接予約も変わる
次は別シナリオとして、利用枠を押さえる各予約の終了後へ10分を加えます。予約の会議終了時刻は変えず、設備を使えない範囲の終了だけを後へ延ばすという、架空の条件です。開始前の準備時間は加えていません。
表は横にスクロールできます
| 予約ペア | 10分追加後の重なる区間 | 重なり |
|---|---|---|
| R1・R2 | 10:00~10:10 | 10分 |
| R1・R3 | 09:45~10:10 | 25分 |
| R2・R3 | 10:00~10:25 | 25分 |
| R2・R6 | 11:00~11:10 | 10分 |
競合するペアは2組から4組へ増えます。これは実際に予約が増えたのではなく、使う条件を変えた結果です。元の条件と結果を消さず、どちらの運用条件で計算したかを併記します。
複数のペアが同じ時間に重なる場合があるため、ペアごとの分数を足して「会議室が競合した合計時間」にしません。また、10分が実施設に必要な片付け時間と確認したわけではありません。実際の条件は、設備の運用担当と確認します。
取得できない会議室を、空きと判定しない
会議室Cの予定が取得できなかったとします。その状態は、予約が0件と確認できたこととは違います。候補の移動先へCを自動で採用しません。
GoogleのFreebusy資料は、カレンダーごとのエラーも返す構造を説明しています。空き情報を使う実装では、対象設備の範囲、取得期間、エラーを確認し、記録がない理由を区別する必要があります。同資料の取得処理や権限を、今回設定したものではありません。
A・Bの表が正しくても、未取得のCや、表へ含まれない設備の空きまで証明できません。仮予約の扱いを含め、異なるシステムから集めた状態名が同じ意味かも確認します。
固定例の競合ペアと分数を検算する
次のPythonは、時刻を同日の0時からの分で表した固定例です。実カレンダーや予約システムへ接続しません。
from itertools import combinations
rows = {
"R1": ("A", 540, 600, "CONFIRMED"),
"R2": ("A", 600, 660, "CONFIRMED"),
"R3": ("A", 585, 615, "TENTATIVE"),
"R4": ("B", 585, 615, "CONFIRMED"),
"R5": ("A", 660, 690, "CANCELLED"),
"R6": ("A", 660, 720, "CONFIRMED"),
}
def conflicts(buffer_minutes):
active = [(key, row) for key, row in rows.items()
if row[3] in {"CONFIRMED", "TENTATIVE"}]
found = {}
for (ka, a), (kb, b) in combinations(active, 2):
if a[0] != b[0]:
continue
overlap = min(a[2] + buffer_minutes, b[2] + buffer_minutes) - max(a[1], b[1])
if overlap > 0:
found[(ka, kb)] = overlap
return found
assert conflicts(0) == {("R1", "R3"): 15, ("R2", "R3"): 15}
assert conflicts(10) == {
("R1", "R2"): 10, ("R1", "R3"): 25,
("R2", "R3"): 25, ("R2", "R6"): 10,
}
print(conflicts(0), conflicts(10))
このコードは、予約IDの重複、未知の状態、逆転時刻、日付違い、取得失敗、負のバッファを検査する汎用処理ではありません。実データでは入力の範囲と状態対応を先に確認します。競合が出なかったことを、入力が全部正しい証拠にはしません。
AIへは、予約変更ではなく確認表を頼む
架空の予約R1~R6から、同一会議室で時間が重なるペアを整理してください。
確定と仮予約を対象、取消は判定対象外とします。元の状態は残してください。
開始を含み終了を含まない区間として、接するだけなら重複0分とします。
片付け時間なしと、各予約の終了後10分を使えない範囲に加える場合を分けてください。
予約ペア、会議室、重なる開始・終了、分数、使った条件を示してください。
異なる会議室を同一設備とせず、未取得の会議室Cを空きと判定しないでください。
予約の優先順位、取消、変更、移動先の確定は行わないでください。
出力:競合候補表、条件による差、未取得、調整前の確認事項。
この依頼と表は手製例で、AIの正答率を測った結果ではありません。確認表ができたら、元の予約へ戻り、会議室、状態、時刻、片付け条件を照合します。取得後に予約が変わる可能性もあるため、調整を決める際は最新状態を確認し、前の候補表を確保済みの記録として使わないようにしてください。
参考資料
公式資料の確認日:2026年10月20日。会議室、予約、状態、片付け条件、競合表は独自の架空例です。掲載Pythonと区間計算をローカルで検査しました。実カレンダー、API取得、AI分析、会議室利用条件、参加者調整、予約変更、画面表示は未検証です。
