MCP のタイムアウトについて前にどこかへ書いたはずなのに、その一箇所が出てこない。エージェントに「タイムアウトの記述を探して」と頼んだら、451 ファイルを候補として並べてきました。全部で 8 MB あります。読み切れる量ではありません。
Antigravity 2.12 で部分一致のファイル名検索が改善されたと知って、この失敗を思い出しました。名前で引けば候補は一桁まで減ります。ただし減った分、何を落としているのかを私は把握していませんでした。手元の 2,088 ファイルのリポジトリで、両方を実際に測ってみます。
同じ言葉で、8 件と 451 件に分かれました
対象は日英2言語の Markdown を 2,088 本、合計 34 MB 抱えているリポジトリです。個人開発で長く書き足してきたもので、規模としてはそれなりに育っています。
「timeout」という一語で、ファイル名検索と全文検索を並べます。
| 方式 | ヒット数 | 全部開いたときの合計 | 所要時間(5回平均) |
|---|---|---|---|
| ファイル名の部分一致 | 8 件 | 128 KB | 5 ms |
| 全文検索(ripgrep) | 451 件 | 8 MB | 15 ms |
検索そのものの速さは 3 倍しか違いません。ミリ秒の世界なので、体感ではどちらも一瞬です。
差が出るのはその後です。エージェントは候補を絞ったあと、中身を読みます。読む量は 67 倍違います。451 ファイル 8 MB を投げれば、コンテキストは埋まり、料金も待ち時間も跳ね上がります。検索方式の選択は、検索の速さの問題ではなく、そのあと何バイト読むかの問題でした。
ripgrep が使えない環境ではこの前提自体が変わります。その場合の検索コストは/codesearch が ripgrep を使えない環境では、検索が何倍遅くなるのか比べましたで別途測っています。
ファイル名に入るのはトピック、本文に入るのは症状です
では名前引きに寄せればよいかというと、そう単純ではありませんでした。エラー文字列やシステムコール名で同じ比較をすると、結果が反転します。
| 語 | ファイル名一致 | 本文一致 |
|---|---|---|
| ENOSPC | 0 件 | 13 件 |
| ECONNREFUSED | 0 件 | 9 件 |
| SIGKILL | 0 件 | 14 件 |
| flock | 0 件 | 18 件 |
| IPv6 | 0 件 | 6 件 |
| ETIMEDOUT | 0 件 | 9 件 |
| EADDRINUSE | 2 件 | 10 件 |
| FORBIDDEN | 2 件 | 9 件 |
| 合計 | 4 件 | 88 件 |
8 語で 4 件しか当たりません。4.5% です。名前引きだけに任せると、エラーを扱った記述の 95% を見ないまま「見つかりませんでした」が返ってきます。
一方で、トピックを表す語なら名前引きは機能します。ollama は名前 16 件に対して本文 121 件で 13%、stripe は 26 件と 249 件で 10%、admob は 22 件と 329 件で 7% でした。それでも 1 割前後です。名前引きは「主題として扱った記事」だけを拾い、「途中で一度触れた記述」は拾いません。
この非対称は実装の癖ではなく、名前の付け方に由来します。ファイル名は書いた本人が主題を要約して付けたラベルです。症状や API 名は本文の中にしか現れません。ファイル名検索がどれだけ賢くなっても、そこに書かれていない文字列は出てこない、という当たり前の制約が残ります。
部分一致は連続した並びしか見ません
もうひとつ、名前引きで踏みやすい落とし穴があります。部分一致は連続部分文字列の照合なので、語順が違うだけで外れます。
同じリポジトリで、MCP のタイムアウトを扱ったファイルを名前から探します。
# 連続部分文字列として引く
find content/articles -iname '*mcp-timeout*' # → 2 件
find content/articles -iname '*timeout-mcp*' # → 0 件
# 語順を問わず、両方を含む名前を引く
find content/articles -iname '*mcp*' -iname '*timeout*' # → 4 件実際のファイル名を見ると理由がわかります。
antigravity-mcp-timeout-seconds-boundary-latency-sizing.mdx
antigravity-cli-mcp-unresponsive-timeout-per-operation-design.mdx2 本目は mcp と timeout の間に unresponsive が挟まっています。*mcp-timeout* では当たりません。語順を入れ替えた *timeout-mcp* に至っては 0 件です。
私はこの挙動を知らないまま「名前検索で 2 件だったので、これで全部です」と判断していました。実際には半分です。-iname を 2 回並べれば AND 条件になるので、複数語で探すときは連続した文字列として書かず、条件を分けるのが安全でした。エージェントに検索を指示するときも同じで、「mcp-timeout を含むファイル」ではなく「mcp と timeout の両方を含むファイル」と伝える必要があります。
frontmatter が全文検索の精度を壊していました
全文検索側にも、Markdown リポジトリ固有の問題がありました。
このリポジトリの各ファイルは先頭に YAML の frontmatter を持っています。premium: false のようなキーがすべてのファイルに入っているため、全文検索が意味をなさなくなります。
# 全文検索(frontmatter を含む)
rg -li premium content/articles -g '*.mdx' | wc -l # → 2023
# 2 本目の --- 以降、つまり本文だけを見る
for f in $(rg -li premium content/articles -g '*.mdx'); do
awk 'c==2{print} /^---$/{c++}' "$f" | grep -qiF premium && echo "$f"
done | wc -l # → 1112,088 ファイル中 2,023 件、97% がヒットします。本文だけに絞れば 111 件でした。20 倍近い水増しです。
frontmatter に現れるキー名やメタ値(premium、category、level、tags など)で全文検索をかけると、ほぼ全件が返ってきます。エージェントにとっては「候補が絞れなかった」ではなく「絞れたつもりで全件を渡された」状態になります。私自身、これに気づくまでの数か月は、検索がうまくいかない原因をクエリの書き方だと思い込んでいました。
Antigravity 2.11.0 以降は frontmatter が整形されたメタデータカードとして描画されるようになり、目で読むぶんには邪魔になりません。ただし検索インデックスの側では変わらず本文の一部です。表示上見えなくなった要素ほど、こういう形で足元をすくいます。
3 段構えにして、再現率と読む量を両方見ました
以上を踏まえて、「MCP のタイムアウトを扱っている記事」を 3 つの方式で探し、結果を突き合わせました。基準は全文検索の AND 条件です。
| 方式 | ヒット数 | 再現率 | 所要時間 |
|---|---|---|---|
| A: 全文で MCP と timeout の AND | 55 件 | 100%(基準) | 36 ms |
| B: 名前を mcp で絞ってから全文で timeout | 18 件 | 33% | 12 ms |
| C: カテゴリで絞ってから全文で AND | 28 件 | 51% | 15 ms |
# A: 全文 AND(基準)
rg -li timeout content/articles -g '*.mdx' | tr '\n' '\0' | xargs -0 rg -lFi 'MCP'
# B: 名前で絞ってから本文を引く
find content/articles -iname '*mcp*' -print0 | xargs -0 rg -lFi 'timeout'
# C: ディレクトリで絞ってから本文を引く
rg -lFi 'MCP' content/articles/*/integrations -g '*.mdx' \
| tr '\n' '\0' | xargs -0 rg -lFi 'timeout'B は速いかわりに 3 分の 1 しか拾えません。C はディレクトリという別の切り口で絞るぶん、半分を保ちます。時間差はいずれも数十ミリ秒で、実務では無視できる範囲でした。
つまり名前引きは「速いから選ぶ」ものではありませんでした。速さでは差がつかず、差がつくのは再現率と、そのあとに読む量です。私はこの数字を見てから、次のように使い分けを決めました。
- 探しているのがエラー文字列・シンボル・API 名のときは、名前引きを使わず最初から全文検索にします。名前に出る確率が 4.5% では、絞り込みではなく取りこぼしです。
- 探しているのが主題としてのトピックで、代表的な 1 本が欲しいだけなら名前引きで十分です。網羅が要る場面には向きません。
- 網羅したいときは、名前ではなくディレクトリで絞ってから全文検索にかけます。再現率が 33% から 51% に上がり、読む量は全文検索の半分に収まります。
Antigravity 2.12 の部分一致検索の改善は、1 と 3 の場面を変えるものではありませんでした。効いてくるのは 2、つまり「あの記事を開きたい」という目的が最初から定まっている場面です。そこは確かに速くなりました。
次に測るなら、自分のリポジトリの命名です
ここで出した 4.5% や 33% という数字は、私のリポジトリの命名習慣が生んだ値です。ファイル名にエラーコードを入れる規約なら、名前引きの当たり方はまるで違ってきます。
なので、この記事の数字をそのまま持ち帰るより、同じ 3 行を自分のリポジトリで走らせるほうが役に立つはずです。エラー文字列を 5 つほど選び、名前一致と本文一致の件数を並べてみてください。比が 1 割を切っていれば、エージェントに名前引きを指示するのは早すぎます。
タイムアウトの値そのものをどう決めるかは、サーバーごとに秒数を書く前に測ったこと — MCP の接続と呼び出しは 486 倍違っていましたに実測を残しています。
数え方の話ばかりで恐縮ですが、検索の入口を間違えると、そのあとの読解も判断も全部ずれていきます。お読みいただきありがとうございました。