AI検索エージェントの曖昧クエリをPythonで止める
2026-07-05にThe DecoderでDiscoBenchの記事を読んだ。211タスク、463個の曖昧ポイントを入れた検索エージェント評価で、トップでもend-to-end accuracyが43.1%。しかも「検索を増やす」だけでは良くならず、曖昧なまま何度も検索して最後に外す、という話だった。
これ、検索エージェントを作っていると地味に刺さる。
自分も今日、手元の小さい検索ラッパーを触っていて、「OpenAI API 料金」みたいな入力をそのまま検索に投げる癖がまだ残っていた。料金は日付で変わるし、比較系の質問は基準が抜ける。なのに検索APIに渡すと、きれいなクエリ文字列になってしまう。見た目は進んでいる。中身はズレている。
ここで欲しいのは賢い検索ではなく、検索前の足止めなんだよね。
AI検索エージェントは検索前に曖昧クエリを止める
自分はここで、検索APIの手前に小さい clarify gate を置くことにした。
やることは単純で、入力を3種類だけ見る。
- エンティティが曖昧か
- 比較基準が抜けているか
- 料金やAPI制限みたいに日付やバージョンが必要か
LLMに判断させる前に、安いルールで止める。全部を救う気はない。むしろ雑でいい。検索を1回増やすより、質問を1回返したほうが事故が小さいケースを拾うための門番。
Pythonで最小のclarify gateを書く
Python 3.11で動かしたコード。外部APIは使っていない。
from dataclasses import dataclass
import re
import unittest
from typing import Literal
Action = Literal["ask", "search"]
AMBIGUOUS_ENTITIES = {
"mercury": "Mercury は惑星、元素、決済会社など複数あります。どれですか?",
"jaguar": "Jaguar は車、動物、スポーツチームなど複数あります。どれですか?",
"go": "Go は言語名、ゲーム、移動の一般語で曖昧です。どの Go ですか?",
"rust": "Rust は言語名、ゲーム、錆の一般語で曖昧です。どの Rust ですか?",
}
CRITERIA_PAT = re.compile(
r"(best|top|fastest|cheapest|compare|ranking|おすすめ|最適|一番|最速|最安|比較|ランキング)",
re.I,
)
CRITERIA_HINT = re.compile(
r"(price|cost|latency|accuracy|license|region|budget|料金|価格|精度|速度|遅延|用途|予算|商用|リージョン)",
re.I,
)
VOLATILE_PAT = re.compile(
r"(api|pricing|price|料金|価格|quota|limit|制限|latest|最新|today|今日)",
re.I,
)
VERSION_OR_DATE = re.compile(
r"(20\d{2}|v?\d+(?:\.\d+){0,2}|today|今日|最新|latest)",
re.I,
)
@dataclass
class Decision:
action: Action
reasons: list[str]
question: str | None
normalized_query: str
def decide(query: str, *, today: str = "2026-07-06") -> Decision:
q = " ".join(query.strip().split())
low = q.casefold()
reasons: list[str] = []
questions: list[str] = []
for word, question in AMBIGUOUS_ENTITIES.items():
if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
reasons.append("entity")
questions.append(question)
break
if CRITERIA_PAT.search(q) and not CRITERIA_HINT.search(q):
reasons.append("criteria")
questions.append("何を基準に比較しますか? 例: 料金、精度、速度、商用利用、運用コスト")
if VOLATILE_PAT.search(q) and not VERSION_OR_DATE.search(q):
reasons.append("version_or_date")
questions.append(f"いつ時点の情報として調べますか? 今日なら {today} 時点で固定します。")
if reasons:
return Decision("ask", reasons, questions[0], q)
return Decision("search", [], None, q)
class ClarifyGateTest(unittest.TestCase):
def test_gate(self):
cases = [
("一番速いベクトルDB", "ask", "criteria"),
("OpenAI API 料金", "ask", "version_or_date"),
("Mercury release date", "ask", "entity"),
("Python 3.12 pathlib Path.walk 使い方", "search", None),
("2026年7月のOpenAI API 料金", "search", None),
("LLM RAG 評価 精度 比較", "search", None),
]
for query, action, reason in cases:
got = decide(query)
self.assertEqual(got.action, action, query)
if reason:
self.assertIn(reason, got.reasons, query)
if __name__ == "__main__":
suite = unittest.defaultTestLoader.loadTestsFromTestCase(ClarifyGateTest)
result = unittest.TextTestRunner(verbosity=1).run(suite)
print("\n--- demo ---")
for query in [
"一番速いベクトルDB",
"OpenAI API 料金",
"Python 3.12 pathlib Path.walk 使い方",
]:
d = decide(query)
print(f"{query} -> {d.action} reasons={d.reasons} question={d.question}")
raise SystemExit(0 if result.wasSuccessful() else 1)
手元ではこう通った。
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
--- demo ---
一番速いベクトルDB -> ask reasons=['criteria'] question=何を基準に比較しますか? 例: 料金、精度、速度、商用利用、運用コスト
OpenAI API 料金 -> ask reasons=['version_or_date'] question=いつ時点の情報として調べますか? 今日なら 2026-07-06 時点で固定します。
Python 3.12 pathlib Path.walk 使い方 -> search reasons=[] question=None
ここで止める理由
検索エージェントの失敗は、最後の要約だけを見ると分かりにくい。検索結果は並ぶし、引用も付く。ログだけ見るとそれっぽい。
でも最初の分岐で「どのMercuryか」「何を速いと呼ぶか」「いつ時点の料金か」を外すと、その後の検索は全部きれいに間違う。これはバグとして厄介で、検索APIの品質問題に見えたり、RAGのランキング問題に見えたりする。実際には入口の仕様が足りないだけ。
なので、検索ツールを呼ぶ直前に Decision("ask", ...) を返せる場所を作る。ここをモデルのプロンプトだけに任せると、「曖昧なら確認して」と書いてあっても、モデルは検索を始めがち。DiscoBenchの記事でも、曖昧さ検知は上がってもタスク成功率がそこまで伸びない、という観察が出ていた。気づくことと、ユーザーに戻すことは別スキルらしい。
自分の見立てでは、検索エージェントの実装はこの順番が扱いやすい。
- ルールで明らかな曖昧さを止める
- 止めた理由をログに残す
- ユーザー回答を受けて検索クエリを作る
- 検索後の要約で引用と日付を確認する
1がないと、4で頑張っても戻れない。検索後に「もしかして別の意味でしたか」と聞くのは、ユーザー体験としてもけっこうしんどい。
実運用で足したいもの
上のコードは雑なので、そのまま本番に入れるなら足すものがある。
まず、曖昧語の辞書はプロダクトごとに持つ。社内検索なら部署名、顧客名、略称、旧サービス名。技術調査ならモデル名、SDK名、クラウドサービス名。ここは一般辞書より、自分たちの検索ログから作ったほうが強い。
次に、ask の数をメトリクスにする。曖昧クエリ率が高い画面は、UI側の入力例が悪い可能性がある。ユーザーが悪いんじゃなくて、聞き方を誘導できていないだけ、というやつ。
あと地味に大事なのが、質問文を短くすること。長い確認文は読まれない。「どのMercuryですか?」くらいでいい。ここで丁寧すぎる文章を出すと、エージェントが急にサポートチャットみたいになる。
おわりに
検索エージェントは、検索が上手いだけでは足りない。曖昧な入力を見つけた瞬間に、いったん止まれるほうが実務では強い。
今回の小さいゲートは、検索品質を上げる魔法ではない。ただ、間違った一本道に入る前にブレーキを踏める。RAGやブラウジングを作っていて「検索回数は多いのに答えがズレる」なら、まず検索前のclarify gateを疑う価値があると思う。