2026-08-20 公開 / AI活用
AIに一覧を作らせると、調べ落としは「該当なし」と同じ顔で返ってくる
20件ほどの対象について「今こういう募集が出ていないか」を毎日AIに調べさせ、一覧にする仕組みを動かしていました。ある日たまたま自分でネットを見ていて、一覧に出ていない募集を見つけました。調べたら、対象20件強のうち14件が、一度も結果に出ていませんでした。壊れてはいません。毎日ちゃんと動いて、毎日それらしい一覧を出していました。原因は指示文のたった1行です。
何が起きていたか
仕組みはよくある形です。調べてほしい対象をリストにして、「この対象それぞれについて、今出ている募集を調べて、決まった形式のJSONで返して」とAIに投げる。返ってきたものを検証して保存し、画面に並べる。
指示文の最後のほうに、私はこう書いていました。
見つからない対象は無理に作らず、その対象は結果から省く。
これはでっち上げを防ぐために書いた行です。実際その目的では機能していました。ありもしない日付やURLを作られるよりずっといい。
ただ、この書き方だと出力はこうなります。
- 本当に募集が無かった対象 → 結果に出ない
- 時間切れなどで調べきれなかった対象 → 同じく結果に出ない
この2つが、受け取る側からは完全に同じ見た目になります。どちらも「その対象の行が無い」だけです。画面には何も出ないので、異常だと気づく材料がありません。エラーも出ません。処理は成功しています。
なぜ調べきれないのか
20件強を1回の指示にまとめて投げていたのが直接の原因でした。AIは上から順に調べますが、全部を回りきる前に「もう十分だろう」という判断で出力に入ります。これは能力の問題というより、1回の応答で使える手数に限りがあるためです。
実際に分けて投げ直したら、それまで一度も出てこなかった対象から、その場で情報が取れました。対象が悪かったのでも、情報が無かったのでもありません。順番の後ろにいたから、そこまで到達していなかっただけです。
そして先ほどの1行があるので、到達しなかった対象は「見つからなかった」として静かに省かれます。ここが噛み合うと、「毎日正常に動いている、中身が欠けた一覧」ができあがります。私の場合、それが半年近く続いていました。
対策1: 対象を分けて、何回かに分けて聞く
いちばん効いたのはこれです。20件強を1回で聞くのをやめ、6件ずつ4回に分けました。1回あたりの担当が減るので、最後まで到達します。
分けるときに気をつけた点が2つあります。
- 1回分が失敗しても、残りは続行する。まとめて1回だと、失敗=全滅です。分けたときに「途中で止まる」実装にすると、分けた意味が半分になります
- 結合はプログラム側でやる。各回の出力から配列を取り出して1本にまとめます。AIの出力には前置きの文やコードフェンスが混ざるので、「最後に現れた配列を採用する」ような寛容な取り出し方にしておくと安定します
分割数は「1回あたり何件までなら最後まで到達するか」で決めます。私は6件にしましたが、対象1件あたりの調査量によって変わります。まず全部を1回で投げたときの結果と、分けたときの結果を比べれば、到達していたかどうかはすぐ分かります。
対策2: 対象ごとに「調べた」記録を残す
分割は再発防止になりません。件数が増えればまた同じことが起きます。必要なのは「調べたのに0件」と「調べていない」を区別できる記録です。
私は対象ごとに、次の3つを別ファイルに持たせました。
- 最後に調査対象に含めた日時(=こちらが聞いた記録)
- 最後に1件でも取れた日(=相手から返ってきた記録)
- 連続で0件だった回数
この3つがあると、状態を4つに分けられます。
- 取得中(今まさに結果がある)
- 今は0件(前は取れていた。ありうる状態)
- 連続0件が続いている(怪しい)
- 一度も取れていない(ほぼ設定側の問題)
そしてこの一覧を画面に出しました。問題のある対象が上に来るように並べています。今回のように「一度も取れていない対象が14件ある」という状態は、この画面があれば初日に分かります。
画面に出すときは「未取得=募集が無いという意味ではない」と明記しておくと良いです。ここを取り違えると、せっかく可視化しても「そういうものか」で流してしまいます。
対策3: 「0件」を失敗として扱わない
これは分割した後に出てきた問題です。分ける前は、全対象を1回で聞くので結果が完全に空になることはまずありませんでした。そのため私は「空の配列が返ってきたら異常」という判定を入れていました。AIが質問を返してきたり、指示を理解できずに何も出さないケースを捕まえるためです。
ところが分割すると、「この6件には今どこも募集が無い」が普通に起きます。正しい答えである空の配列が、毎回「失敗」と判定されるようになりました。私の場合はそこから予備の手段に切り替える作りだったので、正しい0件のたびに予備側を無駄に消費していました。
直し方は、この2つを分けることです。
- 配列が1つも見つからない(質問返し・異常終了)→ 失敗
- 空の配列が返ってきた → 成功(0件という結果)
分割は「1回あたりの担当を減らす」変更ですが、同時に「空が正常になる」という副作用を持ち込みます。判定を見直さないと、分割した分だけ無駄が増えます。
分割で増えるもの
正直に書くと、分けた分だけ呼び出し回数は増えます。1回だったものが4回です。所要時間も、私の環境では全体で2分程度だったものが11分程度になりました。1日1回動かす用途なので許容していますが、頻繁に回すなら効いてきます。
それと、短時間に連続で呼ぶと制限に当たりやすくなります。実際、検証のために立て続けに2回動かしたときは、後半の回が失敗しました。分割は「1回あたりを軽くする」代わりに「呼ぶ回数を増やす」ので、間隔のほうに余裕を持たせておく必要があります。
これは自動化全般の話でもある
今回の件で私が反省したのは、分割の設計より「出力を見て正常だと判断していた」ことのほうです。毎日それらしい一覧が出ていたので、動いていると思っていました。
ですが一覧は「取れたもの」しか映しません。取れなかったものは、定義上そこに映らない。つまり出力を見ている限り、欠損は永久に見えません。見るべきだったのは出力ではなく、「聞いた対象」と「返ってきた対象」の差でした。
これはAIに限った話ではなく、スクレイピングでも、外部APIをまとめて叩く処理でも同じです。収集する自動化を書くときは、成果物とは別に「網羅率」を持つ。今回いちばん学んだのはここでした。
持ち帰れること
- 「見つからなければ省略」と書くと、未調査と0件が同じ出力になる。でっち上げ防止のつもりの1行が、欠損を隠す1行にもなります
- 対象が多いなら分けて聞く。1回で全部を回りきると期待しない。分けた1回が失敗しても残りは続ける作りにする
- 対象ごとに「聞いた日」と「取れた日」を別々に残す。この2つの差が、静かな欠損を見つける唯一の材料です
- 網羅率を画面に出し、問題のある対象を上に置く。出力を眺めていても欠損は見えません
- 分割したら「空の結果」の扱いを見直す。空が正常になるので、異常判定のままだと無駄が増えます
一覧を作る自動化は、動いていることの確認が「一覧が出ていること」になりがちです。ですが一覧が出ていることと、一覧が正しいことは別でした。何が載っていないかを教えてくれる仕組みを、成果物とは別に持っておくのがおすすめです。