ELYSAI
Development

1ページ目で完了にしない、AI業務の「全件処理票」

ELYSAI編集部7分
1ページ目で完了にしない、AI業務の「全件処理票」

一覧APIを呼ぶAIエージェントが、先頭ページだけを処理して「完了」と報告する。これを防ぎたい開発責任者向けに、全ページをたどり、途中停止から安全に戻れる「全件処理票」を作ります。

件数が返っただけでは完了ではない

一覧APIは結果の一部だけを返すことがあります。GitHub REST APIでは、追加ページをlinkヘッダーで示し、rel="next"がなくなるまで取得します。[1] Stripeの一覧APIはstarting_afterを次のカーソルとして使い、has_more=falseを末尾の合図にします。[2]

つまり「レスポンスが200」「配列が空でない」「取得件数がページ上限未満」のどれも、単独では全件完了の証拠になりません。GoogleのAPI設計指針では、要求数より少ない件数、場合によっては0件でも末尾でない応答を認めています。[3]

業務責任者は対象条件と処理の締切を決め、ワークフロー開発者はページ継続、重複排除、再開点を実装します。入力は、エンドポイント、固定する絞り込み条件、並び順、項目ID、次ページの合図、業務更新の冪等キーです。

コピーして使う「全件処理票」

全件処理票は、ページ番号ではなく「どこから読み、何を処理し、末尾を観測したか」を残す実行記録です。次のひな型をジョブ台帳やワークフロー状態へ保存します。

pagination_receipt:
  run_id: ""
  endpoint: ""
  filter: ""
  order: "created_at,id"
  page_size: 100
  cursor_in: null
  cursor_out: null
  page_index: 0
  fetched_ids_hash: ""
  processed_unique: 0
  pending_count: 0
  duplicate_conflicts: 0
  terminal_observed: false
  status: running | paused | complete | needs_review
complete_when:
  - terminal_observed == true
  - pending_count == 0
  - duplicate_conflicts == 0

項目ごとの業務更新にはrun_id + item_id + actionを一意キーとして付けます。同じページを再取得しても、既に成功した項目は読み飛ばせます。カーソルは中身を解釈せず、そのまま保存して返します。ページトークンを不透明な値として扱う設計は、Googleの指針にも明記されています。[3]

実装は五段階で閉じる

  1. 対象を固定する。 実行開始時刻をcutoff_atとして保存し、created_at <= cutoff_atのように範囲を閉じます。並び順は同値を解消できるcreated_at,idにします。
  2. ページを先に記録する。 取得直後、cursor_in、項目IDのハッシュ、cursor_outを保存してから業務処理へ進みます。
  3. 項目単位で確定する。 一意キーを確認し、未処理だけ更新します。失敗項目はpending_countへ残します。
  4. 同じページから再開する。 途中停止したらcursor_inで同じページを取り直し、成功済みを飛ばします。全項目が確定するまで次のカーソルへ進みません。
  5. 末尾を明示的に観測する。 API仕様のhas_more=false、空のnext_page_token、またはrel="next"なしを記録します。末尾観測と未処理0がそろった場合だけcompleteへ変えます。

GitHubは、ページ移動用URLを推測せずlinkヘッダーを使い、一覧には安定した並び順を使うよう勧めています。[4] ページサイズや絞り込み条件も途中で変えません。

【仮想例】期限超過した顧客対応7件を、1ページ3件でCRMへ登録します。1ページ目のA〜Cを確定し、2ページ目でD、Eの後に停止しました。処理票には2ページ目のcursor_inとD、Eの成功が残ります。再開時は同じページを読み直し、D、Eを飛ばしてFを処理します。3ページ目のGを処理して末尾を観測し、ユニーク処理数7、未処理0を確認して完了です。

一覧が途中で変わる失敗を防ぐ

更新日時順の一覧は、処理中の更新で項目が別ページへ移り、重複や欠落を起こします。安定順に変えるか、開始時刻で対象を閉じます。APIがカーソルを提供するなら、オフセットを自作せず公式のカーソルを優先します。

カーソル期限切れや4xxは、値を推測して続行しません。現在の実行をneeds_reviewにし、新しいrun_idで対象を取り直します。429や5xxも完了ではなくpausedです。再試行上限を超えた項目は保留キューへ送り、全体を成功扱いにしません。

対象が必ず一件で、APIが常に単一オブジェクトを返すという場合には向きません。また、一覧が激しく変わり、時点固定も安定順も提供できない場合は、ページ巡回ではなくイベント配信やスナップショット書き出しを選びます。

明日から使える障害試験

試験期待する判定
7件を3件ずつ返す3ページ、ユニーク7件、末尾観測あり
2ページ目の2件後に停止同じページから再開し、業務更新は重複しない
実行中に8件目を追加今回の対象外とし、次回実行で拾う
同じIDを別ページにも返す更新は一度、競合を記録する
has_more=trueなのにカーソルなしneeds_reviewで停止し、完了にしない

まずテスト環境のページサイズを3に下げ、7件を用意します。2ページ目で強制停止し、再開後の業務更新回数と処理票を照合してください。合格条件は「末尾を観測」「対象IDが一度ずつ成功」「未処理0」の三点です。

参考資料

  1. GitHub Docs: Using pagination in the REST API
  2. Stripe API Reference: Pagination
  3. Google AIP-158: Pagination
  4. GitHub Docs: Best practices for using the REST API

本記事はAIを活用して作成し、公開前に自動レビューを実施しています。情報の確認日:2026-10-24。

Share this post

Author

ELYSAI編集部

Talk to us about your AI challenge

Talk to us about your AI challenge

Related Posts