6/26 の更新で、Antigravity の部分文字列検索が目に見えて速くなりました。手元の 2 万ファイル規模のモノレポで、以前は結果が返るまで数呼吸待たされていた検索が、ほとんど待ちを感じなくなっています。
ただ、速度が上がったこと自体より、そこから先の話が私には重要でした。検索が遅いあいだ、私たちは無意識に「エージェントにあまり検索させない」設計を選んでいたのです。速くなったなら、その前提ごと組み直す価値があります。部分文字列検索の高速化を単なる快適さの向上で終わらせず、エージェントにコードを探させる手順そのものをどう再設計するか、個人開発での実測とともに書いていきます。
検索が遅かった頃、私たちは何を諦めていたか
エージェントにコードベースを触らせるとき、探索のやり方は大きく二つに分かれます。生の部分文字列検索(grep 系)で当たりを付ける方法と、意味的な近さで候補を引く検索(埋め込みベース)です。
検索が遅いと、grep 系の一発あたりのコストが重くなります。すると設計は自然と「なるべく検索させず、最初に大きな文脈をまとめて渡す」方向に寄っていきます。私自身、以前は関連しそうなディレクトリをまるごとエージェントに読ませてから作業させていました。これは一見効率的ですが、無関係なファイルでコンテキストを埋め、肝心の判断がぼやける原因になります。
部分文字列検索が速くなると、この天秤が逆に傾きます。**「まず狭く探して、必要な分だけ読む」**という、人間が実際にやっている探索に近い設計が現実的になります。
高速化で何がどれだけ変わったのか、まず測る
体感で語る前に、探索の実コストを数値にしておきます。私が使っているのは、エージェントの 1 タスクあたりの「検索呼び出し回数」「読み込んだ総トークン」「往復回数」を記録する薄いラッパーです。
#!/usr/bin/env bash
# agy-probe.sh — エージェント実行のコストを 1 タスク単位で記録する
# 使い方: ./agy-probe.sh "AdMob の同意フローを consent モジュールに絞って調べて"
set -euo pipefail
TASK="$1"
LOG="probe_$(date +%Y%m%d_%H%M%S).jsonl"
# --json-events でエージェントの内部イベントを行区切り JSON として受け取る
agy run --json-events --prompt "$TASK" | while IFS= read -r line; do
echo "$line" >> "$LOG"
done
# 集計: 検索呼び出し・読み込みトークン・往復回数を抜き出す
python3 - "$LOG" << 'PY'
import json, sys
searches = reads = tokens = turns = 0
for ln in open(sys.argv[1]):
ev = json.loads(ln)
t = ev.get("type")
if t == "tool_call" and ev.get("name") in ("search", "grep"):
searches += 1
if t == "file_read":
reads += 1
tokens += ev.get("tokens", 0)
if t == "assistant_turn":
turns += 1
print(f"searches={searches} file_reads={reads} read_tokens={tokens} turns={turns}")
PY
同じ調査タスクを、更新前後のバージョンを固定して 10 回ずつ流したところ、私の環境では次のような傾向が出ました。数値はあくまで手元のモノレポでの一例です。
| 指標(10 回平均) | 更新前の設計(大きく読ませる) | 更新後の設計(狭く探す) |
| 検索呼び出し回数 | 3.2 | 11.4 |
| 読み込みトークン | 約 48,000 | 約 16,500 |
| アシスタント往復 | 6.1 | 5.4 |
| 誤ったファイルへの編集 | 2 回 | 0 回 |
検索の回数はむしろ増えています。狙いはそこではありません。読み込むトークンを約 66% 減らしつつ、誤編集を消せたことが本題です。検索が安くなったぶん、読む前に絞り込む余地が生まれたわけです。
「grep 先行・意味検索は仕上げ」に配分を戻す
高速化を踏まえた私の現在の既定は、grep 系の部分文字列検索を先に立て、意味検索を仕上げに回す配分です。理由は単純で、コード探索の入り口はたいてい「具体的な識別子」だからです。
関数名、定数、エラーメッセージ、API パスといった文字列は、意味の近さではなく厳密一致で当てるのが最短です。速くなった部分文字列検索は、まさにここに効きます。一方、意味検索は「どこに書いたか思い出せない概念」を探すときに価値が出ます。両者は競合ではなく、順番の問題だと考えています。
エージェントに渡す探索方針は、次のように明文化しておくと安定します。
## 探索の既定手順(AGENTS.md 抜粋)
1. まず既知の識別子(関数名・定数・エラー文言・パス)を部分文字列検索で当てる
2. ヒットしたファイルのうち、定義元と呼び出し元だけを読む(周辺を一括で読まない)
3. 識別子が思い出せない概念は、意味検索で候補を 5 件まで挙げてから grep で確定する
4. 1 タスクで読み込む上限は 20,000 トークン。超えそうなら検索で範囲を割る
この「上限トークン」を明記しておくのが地味に効きます。エージェントは放っておくと安心のために広く読みがちですが、検索が速い前提なら「読むより探す」を選ばせるほうが結果が良くなります。
検索結果を鵜呑みにさせない足場を用意する
速い検索には落とし穴もあります。ヒットが増えると、エージェントが最初に当たった結果へ飛びつきやすくなるのです。私は AdMob の同意まわりを触っていたとき、同名の定数が新旧二か所にあり、古いほうを編集されかけた経験があります。
これを防ぐには、検索の直後に**「範囲を確定させる一手」**を挟みます。ヒット件数が想定より多いときは、まず件数と場所の一覧だけを出させ、編集対象を宣言させてから次に進む、という段取りです。
# 事前に「どこを触るか」を宣言させるためのヘルパー
# grep のヒットをファイル単位に畳んで、件数の多い順に見せる
agy search --pattern "CONSENT_STATUS" --count-by-file \
| sort -t: -k2 -nr \
| head -20
# → エージェントには「この一覧から編集対象を 1〜2 ファイルに絞り、理由を述べてから着手」と指示する
一覧をいったん人間が読めるまとめとして挟むだけで、誤った候補への直行が目に見えて減りました。検索が速くなったからこそ、「速く多く当たる」ことと「正しく絞る」ことを分けて設計する必要があります。
本番運用で古い定数を書き換えてしまうと、原因の切り分けに半日を取られることもあります。私は、ヒットが 10 件を超えたら必ず宣言を挟む運用を推奨します。ここは自動化の速さより、誤りを未然に止める設計を優先すべき箇所だと考えています。
スケジュール実行の常駐タスクにどう組み込むか
私は複数のアプリと 4 つのブログを、夜間のスケジュール実行で少しずつ手入れしています。こうした無人運用では、探索の効率がそのままトークン費用に跳ね返ります。
常駐タスクに探索方針を埋め込むときは、対話時よりもさらに厳しめの上限を置くのが安全です。人が見ていない場面では、エージェントが「念のため」で広く読む挙動が最もコストを膨らませます。
# scheduled-task の探索ポリシー例
search_policy:
substring_first: true # 既知識別子はまず部分文字列検索
max_read_tokens_per_task: 12000 # 無人運用では対話時より低め
semantic_fallback_limit: 5 # 意味検索の候補は 5 件まで
declare_targets_before_edit: true
この設定にしてから、夜間バッチのトークン消費が約 30% 下がり、翌朝ログを見て「なぜこのファイルを読んだのか」が追いやすくなりました。放置運用では、速さそのものより「無駄に読ませない」ことのほうが効きます。
どの規模から効いてくるか
正直に言えば、数百ファイル程度のリポジトリでは、この見直しの恩恵はさほど大きくありません。全体を読ませても破綻しない規模だからです。効果がはっきり出るのは、数千ファイルを超えて「全部は読めない」領域に入ってからでした。
私の場合、壁紙アプリや癒し系アプリのコードは単体では小さくても、共有ライブラリや自動化スクリプトを合わせると探索対象が膨らみます。そこで部分文字列検索の速さが、そのまま「読む量を削れる幅」に変わりました。
小さなプロジェクトなら、まずは計測ラッパーだけ入れて、読み込みトークンが膨らむタスクが出てきた時点で探索方針を足す、という段階的な導入で十分だと感じています。道具の更新に合わせて設計の前提を疑い直すこと、それ自体が長く運用するうえでの投資になると考えています。
同じように大きくなったコードベースと向き合っている方の、手順を組み直すきっかけになれば幸いです。