予約サイトから毎月届く精算明細のPDFを読み取り、予約台帳と突き合わせて、手数料・取消・入金額の差異を理由別に仕分ける
予約サイトから毎月届く日本語の精算明細のPDFを読み取り、予約台帳と予約番号で突き合わせて、手数料・取消料・入金額の差異を理由別に仕分けます。経理の担当者は、差異があると出た予約だけを確かめます。
- 生成AI
- ChatGPT/Claude/Gemini
- AIサービス
- AWS Textract/Azure AI/Google Document AI
- 連携・自動化
- Google Apps Script/Make/n8n/Python
- 対象業界
- 宿泊/飲食
- 対象部門
- 経理/財務
- 対象業務
- 内容確認・チェック/比較検討
- 主な課題
- 人手が足りない/入力作業が多い/確認ミスが多い
- AIで行う処理
- 読み取り(OCR)
- 主な効果
- 入力漏れ削減/品質標準化/工数削減
- 導入難易度
- ★★★☆☆
- 実装レベル
- 本格構成
- 費用感
- API連携(中)
- 人間の確認
- 条件付き
01導入前 / 導入後の業務フロー
- 予約サイトの管理画面から、施設ごとの精算明細のPDFをダウンロードし、共有フォルダに保存する
- PDFを開き、予約番号・宿泊日・宿泊料金・手数料・取消料・精算額を、突合用のスプレッドシートへ転記する
- 予約台帳の一覧を開き、予約番号で1件ずつ探して、宿泊日と料金を見比べる
- 手数料の額を、契約の手数料率から電卓で検算する
- 合わない予約について、予約台帳の変更履歴や備考、フロントの記録を見て理由を調べる
- 理由と差額をスプレッドシートにメモし、予約サイトへ問い合わせるものに印を付ける
- 精算額の合計を、銀行口座への入金額または支払う請求額と見比べる
- 結果を経理の責任者に報告し、会計ソフトへの計上に回す
- 人予約サイトの管理画面から精算明細のPDFをダウンロードし、受付用のフォルダに保存する
- 自動保存をきっかけにスクリプトが動き、ファイル名から施設と予約サイトと対象月を確かめる
- 自動OCRが明細の表とキーと値の組、読み取りの信頼度を返す
- 自動予約サイトごとの列の対応表で、読み取った列を共通の項目にそろえる。対応表に無い列があれば、生成AIが対応の案を出し、人の確認待ちにする
- 自動予約台帳の一覧と、予約サイト側の予約番号で突き合わせる
- 自動手数料・取消料・精算額を契約の条件から計算し、明細の値との差を出す
- 自動差のある予約について、備考欄と予約台帳の変更履歴から、生成AIが理由の候補を選ぶ
- 自動明細の合計と、明細の行を足し上げた額が一致するかを確かめる
- 人担当者が差異の一覧を見て、理由を確かめ、予約サイトへ問い合わせるものを決める
- 人精算額の合計を入金額・請求額と照合し、会計ソフトへの計上に回す
各工程の詳しい説明を読む
- 予約サイトの管理画面から、施設ごとの精算明細のPDFをダウンロードし、共有フォルダに保存する
- PDFを開き、予約番号・宿泊日・宿泊料金・手数料・取消料・精算額を、突合用のスプレッドシートへ転記する
- 予約台帳の一覧を開き、予約番号で1件ずつ探して、宿泊日と料金を見比べる
- 手数料の額を、契約の手数料率から電卓で検算する
- 合わない予約について、予約台帳の変更履歴や備考、フロントの記録を見て理由を調べる
- 理由と差額をスプレッドシートにメモし、予約サイトへ問い合わせるものに印を付ける
- 精算額の合計を、銀行口座への入金額または支払う請求額と見比べる
- 結果を経理の責任者に報告し、会計ソフトへの計上に回す
(a)転記がいちばん時間を食う。 1通に百件近い予約が並ぶ明細を、2番目の手順でスプレッドシートに打ち直しています。PDFから表をコピーすると列が崩れる書式が多く、結局は手で打つことになります。
(b)書式が予約サイトの数だけある。 予約番号の列の位置、手数料が税込か税別か、取消の予約が別の表か同じ表か。8社分の読み方を覚えている担当者は1人だけで、その人が休むと月末の突合が止まります。
(c)差異に気づくのが入金の後になる。 手数料率が契約と違っていた、取消料を受け取ったはずの予約が明細に無い。こうした食い違いは、4番目と5番目を丁寧にやった月にしか見つかりません。 忙しい月は合計が合っていれば通してしまい、個々の食い違いが相殺されて埋もれます。
(d)理由を調べるのに時間がかかる。 差額が出た予約について、泊数の変更なのか、無断の不泊なのか、現地での追加料金なのかは、明細だけでは分かりません。予約台帳の履歴とフロントの記録を行き来することになります。
- 【人】 予約サイトの管理画面から精算明細のPDFをダウンロードし、受付用のフォルダに保存する
- 【自動】 保存をきっかけにスクリプトが動き、ファイル名から施設と予約サイトと対象月を確かめる
- 【自動】 OCRが明細の表とキーと値の組、読み取りの信頼度を返す
- 【自動】 予約サイトごとの列の対応表で、読み取った列を共通の項目にそろえる。対応表に無い列があれば、生成AIが対応の案を出し、人の確認待ちにする
- 【自動】 予約台帳の一覧と、予約サイト側の予約番号で突き合わせる
- 【自動】 手数料・取消料・精算額を契約の条件から計算し、明細の値との差を出す
- 【自動】 差のある予約について、備考欄と予約台帳の変更履歴から、生成AIが理由の候補を選ぶ
- 【自動】 明細の合計と、明細の行を足し上げた額が一致するかを確かめる
- 【人】 担当者が差異の一覧を見て、理由を確かめ、予約サイトへ問い合わせるものを決める
- 【人】 精算額の合計を入金額・請求額と照合し、会計ソフトへの計上に回す
6番目を生成AIではなくスクリプトで行うのが、この設計の要です。 契約の手数料率と明細の手数料の額が合っているかは、掛け算と引き算で決まります。計算は決まった規則で行い、生成AIには「なぜ合わないか」の候補だけを選ばせます。
8番目の検算は、読み取りの誤りを見つけるためのものです。 明細の最後に印字された合計と、読み取った行を足し上げた額が合わなければ、どこかの行を読み落としているか、読み違えています。 そのときは突合の結果を信用せず、先に読み取りを確かめます。
02今回想定するシステム構成
精算明細のPDF(予約サイト8社 × 3施設) │【トリガー】受付用フォルダへの保存 ▼ Google Apps Script ── ファイル名から施設・予約サイト・対象月を確認 ▼ Google Document AI(Form Parser) │ 表(headerRows/bodyRows)、キーと値の組、信頼度を返す ▼ Google Apps Script ── 予約サイトごとの列の対応表で共通の項目にそろえる │ └─ 対応表に無い列 ──▶ Gemini API(対応の案)──▶【人の確認】 ▼ Google Apps Script ── 予約台帳と予約番号で突合、手数料・取消料・精算額の差を計算 ▼ Gemini API ── 差のある予約の理由の候補(備考欄・変更履歴から選ぶ) ▼ 突合結果のスプレッドシート(差異の一覧・理由の候補・合計の検算) ▼ 【人】理由の確認、問い合わせの判断 ──▶ 会計ソフトへの計上
| 役割 | 想定する製品 | 代替候補 |
|---|---|---|
| OCR | Google Document AI(Form Parser) | Azure AI Document Intelligence(レイアウトモデル)、AWS Textract(英文の明細に限る) |
| 生成AI | Gemini API(列の対応の案と、差異の理由の候補) | Claude API、OpenAI API |
| 差異計算 | Google Apps Script(突合と、手数料・取消料・精算額の差の計算) | Python |
| 連携 | Google Apps Script(フォルダの監視、対応表の読み込み、結果の書き出し) | Make、n8n |
| 保管 | Google ドライブ(精算明細と突合結果) | SharePoint、Box |
予約管理システムと会計ソフトは、今のまま使います。 予約台帳は予約管理システムから書き出した一覧を読むだけで、この構成から予約管理システムにも会計ソフトにも書き込みません。
OCRに Form Parser を選ぶのは、日本語の書類から表を取り出せるからです。 Document AI の処理プロセッサの一覧では、Form Parser はキーと値の組(項目とチェックボックス)、表、一般的なエンティティを文書から取り出すものとされ、対応言語に日本語が含まれています。精算明細は「対象期間」「施設名」「お支払い予定日」のようなキーと値の組と、予約が並ぶ表の2つでできているので、Form Parser の返す形にそのまま合います。
生成AIによる項目の取り出しを担う Custom Extractor は使いません。 生成AIによる取り出しの正式な対応は英語に限られているためです。日本語の明細は Form Parser で表として読み、列の意味づけは対応表と Gemini API で行います。
Form Parser の表の読み取りには、1つ大きな制約があります。 処理結果の説明では、Form Parser の表の取り出しは行や列をまたぐセルの無い通常の表だけを認識し、rowSpan と colSpan は常に1になるとされています。見出しが2段になっていて「手数料」の下に「率」「額」が並ぶような明細は、見出しの対応がずれます。これが対応表を予約サイトごとに持つ理由の1つです。
03どうやって実装するのか
処理の起点を決める
受付用フォルダにPDFが保存されたことを起点にします。 精算明細は月末から月初に集中して届くため、週1回や月1回のまとめ処理にはしません。 届いた順に処理すれば、最後の1通が届いた時点で大半の突合が終わっています。
Google Apps Script の時間主導のトリガーで、数分ごとに受付用フォルダを見て新しいファイルを拾います。処理が終わったファイルは、施設と月ごとの処理済みのフォルダへ移します。 移すのは突合の結果を書き出せたときだけにします。受付用フォルダに残っている数が、未処理の数です。
ファイル名は「施設コード_予約サイトコード_対象月.pdf」の形で保存する運用にします。予約サイトと施設をPDFの中身から推し量らないためです。 列の対応表を選ぶ鍵になるので、ここを人の運用で固めます。
入力データを集める
| データ | 中身 | 取得元 |
|---|---|---|
| 精算明細のPDF | 予約サイトの管理画面から取得したもの。施設・予約サイト・対象月 | 受付用フォルダ |
| 読み取り結果 | 表の見出しの行と本体の行、キーと値の組、読み取りの信頼度 | Google Document AI |
| 列の対応表 | 予約サイトごとに、明細の列名と共通の項目の対応。手数料が税込か税別か、取消料に手数料がかかるか | 経理が持つスプレッドシート |
| 契約の条件 | 予約サイトごと・施設ごとの手数料率、精算の形(予約サイトが受け取るか、施設が受け取るか) | 経理が持つスプレッドシート |
| 予約台帳の一覧 | 予約サイト側の予約番号、宿泊日、泊数、宿泊料金、取消の有無と取消料、変更履歴、備考 | 予約管理システムの書き出し |
質を決めるのは、予約台帳の「予約サイト側の予約番号」です。 突合の鍵はこの番号だけで、宿泊者名や宿泊日で突き合わせると同姓同日の予約で取り違えます。番号が台帳に入っていない予約は、この構成では突き合わせません。 「台帳に番号なし」として人に回します。
契約の条件は、改定の日付を持たせて管理します。 手数料率はキャンペーンや契約の見直しで変わります。宿泊日で見るか、予約日で見るかは予約サイトとの契約によるので、その区分も条件の表に持たせます。
データの取得方法を決める
読み取りは、スクリプトから Form Parser を呼びます。精算明細は予約の件数によってページ数が変わるので、ページ数で呼び方を分けます。
| ページ数 | 呼び方 |
|---|---|
| 15ページ以下 | オンライン(同期)処理 |
| 16〜30ページ | オンライン処理で imageless_mode を有効にする |
| 31〜100ページ | バッチ処理 |
| 100ページ超 | ページで分割してからバッチ処理 |
Form Parser の上限は、オンライン処理で15ページ・40MB、imageless_mode を有効にすると30ページ、バッチ処理で100ページ・1GBとされています。imageless_mode の30ページは、1ページ目から連続して処理する場合に限られます。 途中のページだけを投げると15ページの上限に戻ります。
| 取るもの | どこから | 何に使うか |
|---|---|---|
| 表の見出しと本体の行 | tables の headerRows と bodyRows | 予約ごとの行 |
| キーと値の組 | formFields の fieldName と fieldValue | 対象期間、施設名、合計、支払予定日 |
| 全文 | text | 表から漏れた行、脚注の拾い直し |
| 信頼度 | 各要素の layout の confidence | 読み取りの誤りの疑い |
表はページごとに返るので、ページをまたいで1つの表につなぎ直します。 2ページ目以降にも同じ見出しが印字されている明細が多く、見出しの行が本体の行として混ざらないよう、見出しの文字列が一致する行を落とします。 取消の予約が別の表に分かれている明細は、表ごとに「通常」「取消」の区分を付けてからつなぎます。
予約台帳は、予約管理システムから対象月の一覧を書き出し、スプレッドシートとして同じフォルダに置きます。書き出しの抽出条件は、宿泊日で対象月に入る予約と、対象月に取り消された予約の両方にします。 取消の予約を落とすと、取消料の差異がすべて「台帳にない予約」になります。
AIへ渡す前に整形する
- ファイル名の確認 … 施設コード・予約サイトコード・対象月が読めないファイルは処理せず、担当者に知らせます
- 形式の確認 … PDFであることを確かめます。予約サイトの画面を撮った画像が混ざっていたら、PDFを取り直してもらいます
- ページ数の確認 … 上の表のとおりに呼び方を選び、100ページを超えるものは分割します
- 重複の確認 … 同じ施設・予約サイト・対象月の明細がすでに処理済みなら、差し替え版かを担当者に確かめます
- 読み取り後の見出しの照合 … 読み取った見出しの列名を、その予約サイトの対応表と照らします。1つでも一致しない列があれば、対応表の更新待ちにして突合を止めます
- 金額と日付の正規化 … 「¥12,300」「12,300円」「△1,200」を数値に、「2026/9/3」「9月3日」を日付にそろえます。「△」とマイナス記号は負の数として扱います
5番目で止めるのが、この構成で最も大事な前処理です。 予約サイトが書式を変え、「手数料」の列が「手数料(税込)」に変わっただけでも、税別として計算すれば全件に差異が出るか、逆に全件の差異が消えます。 列が1つでも見慣れなければ、突合を進めません。
AIに処理させる
生成AIにさせる仕事は2つです。 対応表に無い列が出たときに対応の案を出すことと、差のある予約について理由の候補を選ぶことです。表の読み取りはOCR、突合と計算はスクリプトが行います。
| させること | 中身 |
|---|---|
| 列の対応の案 | 新しい列名が、共通の項目(予約番号、宿泊日、泊数、宿泊料金、手数料、取消料、ポイント負担、精算額など)のどれに当たるか。税込か税別かの手がかり |
| 差異の理由の候補 | 決められた理由の一覧から、明細の備考欄と予約台帳の変更履歴・備考に合うものを選ぶ |
| 根拠の書き出し | 理由を選ぶ根拠にした文字列を、明細と台帳からそのまま写す |
差異の理由は、次の一覧から選ばせます。
| 理由コード | 中身 |
|---|---|
rate_mismatch | 手数料率が契約の条件と違う |
cancel_fee | 取消料の有無・額が台帳と違う |
no_show | 無断の不泊の扱いが台帳と違う |
stay_changed | 泊数・人数・プランの変更が明細に反映されていない |
point_coupon | ポイント・クーポンの負担の扱いが違う |
not_in_ledger | 明細にあるが台帳にない |
not_in_statement | 台帳にあるが明細にない |
unknown | 上のどれにも当てはまらない |
| させないこと | 理由 |
|---|---|
| 手数料・差額の計算 | 計算の誤りが「差異なし」に紛れる。スクリプトで行う |
| 対応表の更新 | 案を出すだけ。対応表を直すのは経理の担当者 |
| 予約の突合(どの予約とどの行が同じか) | 予約番号で機械的に決める |
| 予約サイトへの申し立ての判断 | 予約サイトとの関係と契約の解釈に関わる |
| 一覧に無い理由を作ること | unknown として人に回す |
1行目がいちばん起きやすい失敗です。 明細の行と台帳の行を渡して「差異を調べて」と頼むと、生成AIは自分で手数料を計算し直し、その計算が合っていれば差異なしと書きます。 差の有無と額はスクリプトで決めてから渡し、生成AIには「この差の理由はどれか」だけを問います。
Gemini API では、スキーマを指定してJSONで受け取ります。 理由コードを enum として指定すれば、一覧に無い値は返りません。ただし構造化出力の説明でも、JSONとして正しくても中身が誤っていることがあるので、アプリケーションの側で値を検証するよう求められています。理由の候補は、根拠の文字列が明細と台帳に本当にあるかをスクリプトで確かめてから一覧に載せます。
指示内容を固定する
あなたは宿泊施設の経理担当で、予約サイトとの精算の食い違いの理由を調べる立場です。
渡された情報だけを見て、差異の理由の候補を選んでください。推測で埋めないでください。
【予約サイト】{ota_name} 【精算の形】{settlement_type}
【明細の行】{statement_row} … OCRで読み取った1予約分の行と備考欄
【台帳の行】{ledger_row} … 予約台帳の同じ予約番号の行、変更履歴、備考
【計算結果】{diff} … スクリプトが計算した手数料・取消料・精算額の差
【契約の条件】{contract_terms} … この予約に適用される手数料率と取消料の扱い
【理由コード】
rate_mismatch / cancel_fee / no_show / stay_changed /
point_coupon / not_in_ledger / not_in_statement / unknown
【厳守事項】
- 金額の計算をしないでください。差の有無と額は【計算結果】をそのまま使ってください。
計算し直して「差異なし」と判断しないでください。
- 理由は理由コードの一覧から選んでください。一覧に無い理由を作らないでください。
当てはまるものが無いときは unknown にしてください。
- 理由を選んだ根拠の文字列を、明細の行か台帳の行からそのまま写してください。
根拠の文字列が無いときは、理由を選ばず unknown にしてください。
- 備考欄の略語(「NS」「泊変」など)を推測で読み替えないでください。
意味が決められないときは unknown にしてください。
- 予約サイトへ申し立てるべきかどうかは書かないでください。
- 【精算の形】が「施設が受け取る」のときは、差の向きが逆になることに注意してください。
向きの判断は【計算結果】の符号に従ってください。
「計算し直して差異なしと判断しない」を明記しないと、計算結果が無視されます。 生成AIは渡された数字から自分で手数料を出し直し、丸めの違いで差が消えれば「差異なし」と書きます。 差の有無はスクリプトが決めた事実として渡し、それを覆す経路を指示の上でも塞ぎます。
「根拠の文字列が無ければ unknown」は、理由の作り話を防ぐためです。 差額が宿泊料金の1泊分なら、根拠が無くても stay_changed を選びがちです。根拠の文字列を写させ、スクリプトでその文字列の存在を確かめることで、もっともらしい推測を一覧から外します。
出力形式を固定する
予約1件ごとに、次の形のJSONを作ります。 diff と match はスクリプトが入れ、reason と evidence は生成AIが入れます。
{
"statement_id": "H02_OTA05_2026-09",
"ota_reservation_no": "",
"match": "matched | not_in_ledger | not_in_statement",
"statement": { "stay_date": "", "nights": 0, "room_charge": 0,
"commission": 0, "cancel_fee": 0, "payout": 0,
"confidence_min": 0 },
"ledger": { "stay_date": "", "nights": 0, "room_charge": 0,
"cancelled": false, "cancel_fee": 0 },
"diff": { "commission": 0, "cancel_fee": 0, "payout": 0 },
"reason": "rate_mismatch | cancel_fee | no_show | stay_changed | point_coupon | not_in_ledger | not_in_statement | unknown",
"evidence": "",
"status": "ok | review | reread"
}
1つ目の理由は、diff と reason の書き手を分けていることです。 差の額はスクリプト、理由は生成AIが入れます。理由の一覧を変えても、差の計算には触れません。
2つ目は、confidence_min を持たせることです。 その行の金額の要素のうち、いちばん低い読み取りの信頼度を入れます。信頼度が低い行の差異は、理由を調べる前に読み取りを疑います。
3つ目は、status で人の見る順番を決めることです。
status | 条件 | 担当者の扱い |
|---|---|---|
ok | 差がすべて0で、match が matched | 一覧で件数だけを見る |
review | 差がある、または match が matched 以外 | 理由の候補と根拠を確かめる |
reread | 信頼度が基準を下回る金額がある、または合計の検算が合わない | PDFの該当行を目で見て、読み取りを直してから突合し直す |
システムへ連携する
| つなぎ先 | 方式 | 内容 |
|---|---|---|
| 受付用フォルダ | Google Apps Script の時間主導のトリガー | 新しいPDFを拾う |
| Google Document AI | API呼び出し | 表・キーと値の組・信頼度を返す |
| 列の対応表・契約の条件 | スプレッドシートの読み取り | 予約サイトごとの列の意味と手数料率 |
| 予約台帳の一覧 | スプレッドシートの読み取り | 予約サイト側の予約番号で突き合わせる |
| Gemini API | API呼び出し | 列の対応の案と、差異の理由の候補 |
| 突合結果のスプレッドシート | 書き出し | 予約ごとの結果と、明細ごとの集計 |
予約管理システムと会計ソフトへは書き込みません。 差異が見つかっても、台帳を直すのはフロントか予約の担当者で、会計ソフトへの計上は人が確かめた精算額で行います。 突合の結果から会計ソフトへ直接書く経路を作ると、読み取りの誤りがそのまま売上の数字になります。
列の対応表の更新も人が行います。生成AIの案は、対応表の横の「案」の列に書き出すだけにし、担当者が確かめて本表へ移します。
人が確認する
人が開くのは review と reread の予約だけです。 ok の予約は件数と合計額を一覧で見ます。
rereadを先に見る … 読み取りの疑いがある行です。PDFの該当行と見比べ、読み取りを直してから突合し直します- 合計の検算を見る … 明細に印字された合計と、行を足し上げた額が合っているかを明細ごとに見ます。合わない明細は、行の読み落としを探します
reviewの理由と根拠を確かめる …evidenceの文字列を明細と台帳で確かめます。unknownはフロントの記録まで当たります- 予約サイトへ問い合わせるものを決める … 理由と差額から判断します。問い合わせの判断は担当者が行います
- 精算額の合計を入金額・請求額と照合する … 照合が済んだ明細だけを計上に回します
2番目を省かないでください。 行を1つ読み落とすと、その予約は not_in_statement として差異に出ますが、読み落とした行の金額が小さいと、理由を調べるうちに「予約サイト側の漏れ」と判断してしまいます。 合計が合わない明細は、差異を見る前に読み取りを確かめます。
例外に対処する
| 起きること | 対応 |
|---|---|
| ファイル名から施設・予約サイト・月が読めない | 処理せず担当者に知らせる |
| 対応表に無い列が出た | 突合を止め、生成AIの案を対応表の横に書き出して担当者の確認を待つ |
| 見出しが2段で列の対応がずれる | 行や列をまたぐセルは認識されない。対応表に列の位置で対応を書く |
| 30ページを超える明細 | バッチ処理(100ページ・1GBまで)に回す。超えるものは分割 |
| 合計の検算が合わない | 明細全体を reread にし、突合の結果を出さない |
| 予約台帳に予約サイト側の予約番号が無い | 突き合わせず not_in_ledger の候補として人へ |
| 同じ予約番号が台帳に2件ある | 予約の分割や再予約の可能性。自動で選ばず人へ |
| 差し替え版の明細が届いた | 前の版の結果を残したまま、新しい版で突合し直し、差分を示す |
| OCRが応答しない | 受付用フォルダに残す。処理済みへ移すのは成功時だけ |
上から2行目と3行目が、書式の変化への備えです。 予約サイトの明細の書式は予告なく変わることがあり、変わった月に全件の差異が消える・全件に差異が出るのが最も気づきにくい失敗です。列が見慣れなければ止める、という1つの規則で両方を防ぎます。
記録を残す
- 精算明細のPDFと、取得した日時・取得した担当者
- Document AI が返した結果の全文(表、キーと値の組、信頼度)
- 突合に使った列の対応表と契約の条件のその時点の版
- 予約ごとのJSON(
diff、reason、evidence、status) - 担当者が理由を直した記録と、予約サイトへ問い合わせた結果
- 明細ごとの合計の検算の結果
3つ目で対応表と条件の版を残すのは、手数料率が後から変わるためです。 契約の見直しで率が変わった後に過去の月を見直すと、当時の率が残っていなければ、差異があったのかどうかが決まりません。
04実装レベルの3段階
最小構成では24通がさばけません。 明細を1通ずつ入れて貼り付けるので、確かめるための段階です。 半自動化で、1通120分が50分程度になります。 転記と突合と計算はなくなりますが、差異の理由を1件ずつ調べる作業が残ります。本格構成で30分になり、この段階が本記事の想定です。 差が大きいのは、理由の候補と根拠が先に並ぶので、台帳の履歴を探しに行く回数が減るからです。 半自動化の段階で、列の対応表を8社分そろえてください。 対応表が不完全なまま理由の仕分けまで足すと、列の取り違えで出た差異に、生成AIがもっともらしい理由を付けてしまいます。
05工数削減シミュレーション
導入後 24件 × 30分 ÷ 60 = 12 時間/月
自社条件で導入効果を整理したい方へ
このユースケースを自社に当てはめた場合の前提値と削減見込みを、業務ヒアリングをもとに整理します。
06向いている企業・向いていない企業
- 複数の予約サイトに客室を出している旅館・ホテル。予約サイトから毎月、日本語のPDFで精算明細が届き、書式が予約サイトごとに違う場合。明細の予約番号と金額を予約台帳へ手で突き合わせており、手数料の率の違いや取消料の扱いに気づくのが入金の後になっている場合。予約台帳に予約サイト側の予約番号が記録されている場合。
- 予約サイトが1〜2社で、精算の件数が月に数十件にとどまる施設。予約サイトの管理画面から精算の明細をCSVで取得でき、PDFを読む必要がない場合。予約台帳に予約サイト側の予約番号が無く、予約を突き合わせる鍵が無い場合。なお、差異を予約サイトへ申し立てるかどうかの判断は、この構成では代替できません。
07最小構成で試す方法
- 先月の精算明細から、書式の違う予約サイト3社分を選ぶ(差異があったと分かっている明細を1通は入れる)
- 同じ月の予約台帳の一覧を書き出す
- 明細のPDFを手元の生成AIサービスに入れ、「表をそのまま、予約番号・宿泊日・宿泊料金・手数料・取消料・精算額の列で書き出してください。読めない値は空欄にしてください」と指示する
- 書き出された表をスプレッドシートに貼り、予約台帳と予約番号で突き合わせる。手数料と差額の計算はスプレッドシートの数式で行う
- 当時の手作業の突合結果と、差異の予約が一致するかを見る
3番目で、生成AIに突合や計算までさせないでください。 最小構成で確かめたいのは、書式の違う明細から表を正しく取り出せるかです。
| 出てきた内容 | 判断 |
|---|---|
| 当時と同じ差異の予約が出た | OCRと対応表の構築に進む |
| 見出しが2段の明細で列がずれた | 対応表で列の位置を固定すれば直る。構成は有効 |
| 台帳と突き合わない予約が多い | 予約台帳の予約番号の整備が先。AIの問題ではない |
08実装時につまずきやすいポイント
| 問題 | 対策 |
|---|---|
| 生成AIが手数料を計算し直して差異を消す | 計算はスクリプトで行い、計算し直しを指示で禁じる |
| 書式が変わった月に全件の差異が消える | 見慣れない列が1つでもあれば突合を止める |
| 見出しが2段の明細で列がずれる | 行や列をまたぐセルは認識されない。対応表に列の位置で対応を書く |
| ページごとに見出しの行が混ざる | 見出しの文字列が一致する行を落としてからつなぐ |
| 30ページを超えてオンライン処理が失敗する | imageless_mode は1ページ目から連続して処理する場合だけ30ページ。超えればバッチ処理 |
取消の予約が全部 not_in_ledger になる | 予約台帳の書き出しに、対象月に取り消された予約を含める |
| 同姓同日の予約で取り違える | 予約サイト側の予約番号だけを鍵にする |
| 「△1,200」を正の数として読む | 正規化で負の数として扱う |
| 手数料率の改定が反映されない | 契約の条件に改定日を持たせ、宿泊日か予約日かの区分も持つ |
| 理由の候補がもっともらしい推測になる | 根拠の文字列を写させ、スクリプトで存在を確かめる |
| 生成AIの対応の案がそのまま対応表に入る | 案の列に書くだけにし、人が本表へ移す |
上の3行が、この構成の失敗のほとんどです。 どれも結果の一覧が「差異なし」または「全部差異あり」という形で出るため、一見すると正常に見えるか、明細の側の問題に見えます。 計算の置き場所と、列の照合で止める規則を、最初の設計で固めてください。
09セキュリティ・AIガバナンス上の注意点
この構成で扱うデータ: 予約サイト側の予約番号、宿泊日と泊数、宿泊料金、手数料、取消料、そして明細や予約台帳に含まれる宿泊者の氏名です。
- 生成AIに渡す範囲を理由の判断に要る項目に限る … 理由の候補を選ぶのに宿泊者の氏名は要りません。明細の行と台帳の行から氏名の列を落としてから渡します
- 予約台帳を丸ごと渡さない … 渡すのは差のある予約の行だけです。突合はスクリプトの側で行い、台帳の一覧そのものを生成AIへ送りません
- 予約サイトへの申し立てを自動にしない … 差異の理由は候補にすぎません。問い合わせるかどうか、どう伝えるかは担当者が決めます
- 会計ソフトへの計上を自動にしない … 読み取りの誤りが売上の数字になる経路を作りません。計上は人が確かめた精算額で行います
- 精算明細のPDFの保管先を決める … 予約サイトから取得した明細は、精算の根拠になる書類です。取得したPDFと、読み取った結果のどちらを根拠として残すかを先に決めておきます
- 管理画面へのログインの情報を共有しない … 明細の取得は人が行う手順にしてあります。予約サイトの管理画面のIDとパスワードを、スクリプトに持たせないでください
誤りが起きた場合のリスクは、差異を見落として入金を受け入れることと、差異でないものを予約サイトへ申し立てることの2つです。 前者は計算を生成AIに任せると起き、後者は読み取りの誤りを差異と取り違えると起きます。計算はスクリプト、読み取りの疑いは reread という置き場所で、両方を設計で防ぎます。
10まず何から始めるか
1週目:予約台帳の予約番号を確かめる
予約管理システムから先月の一覧を書き出し、予約サイト経由の予約に予約サイト側の予約番号がそろっているかを経路ごとに確かめます。番号が入っていない経路の一覧が、この週の成果です。
2週目:3社分で試す
書式の違う予約サイト3社の明細を選び、手元の生成AIサービスで表を書き出させて、スプレッドシートで突き合わせます。当時の手作業の結果と、差異の予約が一致するかを見ます。
3週目:列の対応表と契約の条件を作る
予約サイト8社について、明細の列名と共通の項目の対応、手数料が税込か税別か、取消料の扱い、手数料率と改定日をまとめます。見出しが2段の明細は、列の位置で対応を書きます。
4週目:Form Parser から突合までをつなぐ
Google Apps Script で受付用フォルダを見張り、Form Parser を呼び、対応表で列をそろえて予約台帳と突き合わせ、差の計算までを書き出します。この時点では理由の候補を出さず、差の一覧と合計の検算だけを見ます。
2か月目: 差異の理由の候補と status による振り分けを足し、review と reread の件数を明細ごとに数えます。3か月目以降: 1通120分が何分になったかを実測します。予約サイトごとの差異の理由の傾向が見え、精算の取り決めを確かめる材料がそろった時点で、この構成は完成です。
11関連ユースケース
12この仕組みを理解するための記事
13技術仕様の確認日・参考情報
| 確認した内容 | 情報源 | 確認日 |
|---|---|---|
| Form Parser がキーと値の組(項目とチェックボックス)、表、一般的なエンティティを文書から取り出し、対応言語に日本語が含まれること。Enterprise Document OCR と Layout Parser も日本語に対応すること。Custom Extractor の生成AIによる取り出しの正式な対応が英語に限られること | Google Cloud: Processor list | 2026-09-30 |
応答の text が全文で、他の要素がその位置を参照すること。表が headerRows と bodyRows とセルで表され、Form Parser の表の取り出しは行や列をまたぐセルの無い通常の表だけを認識し rowSpan と colSpan が常に1であること。formFields の fieldName と fieldValue。各要素の layout に信頼度があること | Google Cloud: Handle the processing response | 2026-09-30 |
Form Parser の上限がオンライン処理で15ページ・40MB、imageless_mode を有効にすると30ページ、バッチ処理で100ページ・1GBであること。imageless_mode の拡張は1ページ目から連続して処理する場合に限られること | Google Cloud: Document AI quotas and limits | 2026-09-30 |
Gemini API でJSONのスキーマを指定して構造化された出力を受け取れ、文字列の enum を指定できること。出力がJSONとして正しくても中身が誤っていることがあり、アプリケーションの側で値を検証するよう求められていること | Gemini API: Structured output | 2026-09-30 |
差異を予約サイトへ申し立てるか、どう計上するかは、自社の経理と予約サイトとの契約に従って判断してください。 本記事は上記の公開情報で確認できた範囲だけを扱っています。
実装ステータス:構成例。 公開仕様に基づいて設計した構成であり、当社で実際に構築・検証したものではありません。工数の数値はモデル条件による試算です。
自社の業務に使えるAI活用候補を整理します
このユースケース(UC-0393)についてのご相談はこちらから。
