ツールチュートリアル · 小互が見た

関数名を変えるだけで、14種類のモデルが同じコードを読む際のトークン消費を最大3分の2削減できた

彼らがコードを探す方法は最も原始的。リポジトリ全体のテキストをスキャンして一致する箇所を探します。名前が曖昧だと、読むべきファイルが数百個に膨れ上がります
1分でわかる要点
  • 関数に付ける名前次第で、AIが修正するときに読むファイル数が19個にも459個にも変わります。Modemは1,680回の対照実験でこのコストを明らかにしました
  • 根本原因はここにあります。AIはコードを探すのにプレーンテキスト検索を使います。コンパイラが描く依存関係グラフも、シンボルを解析する言語サーバーもありません。Claude Codeチームは初期にベクターデータベースを試しましたが、結局捨てました
  • 名前を一発で見つかるものに変えると、14種類のモデル・ツール組み合わせ全てでトークン消費が減少。最大削減ケースでは平均7万→2.4万トークン。中央値で3割削減。しかも、自信満々に間違えるケースはゼロになりました
  • 節約できた検索予算は、そのまま判断力に変わります。同じ8ラウンドの調査予算でバグ探しをさせたところ、弱いモデルは巨大ファイルでは8個中3個しか見つけられませんでしたが、コードを分割しリネームした後は8個全てを発見。失敗したレビューのほとんどは誤った回答をしたわけではなく、予算を全てファイル探索に費やし、結論にたどり着けていませんでした
⚑ 出典はModemのエンジニアリングブログ。実験は彼ら自身の設計・実施で、文末に製品プロモーションあり。原文はテスト上の限界もいくつか明記しており、本サイトも文末に併記します。
1 根底にある事実

AIは一体どうやってコードを探しているのか?(基本メカニズム)

コーディングAIに「この関数名を変えて」と頼むと、彼らはまずあなたのリポジトリを検索します。

この「検索」には何ら高度な仕組みはありません。リポジトリ全体のテキストを走査し、指定した文字列が含まれる行を探します。エディタでCtrl+Fを押すのとまったく同じです。この操作はコマンドラインではgrepと呼ばれ、数十年前から存在します。AIが使うのはその高速版ripgrep(コマンドはrg)です。

彼らはコンパイラに依存関係グラフを尋ねたり、言語サーバーにシンボルを解決してもらったりはしません。彼らが認識するのは文字通り表記だけです。検索し、ヒット箇所の前後を読み、足りなければ別の言葉で再検索します。

Claude Codeがリポジトリ内を検索している画面。実行しているのはripgrepで、ヒット箇所を読んで、足りなければ再検索します。動画出典:Modem

これは特定のツールが怠けているわけではありません。Claude Codeチームは初期にベクターデータベース方式を試しましたが、結局捨てました。プレーンテキスト検索の方が優れていたからです。学界発のSWE-agentも同じ結論に至っています。モデルに与えられているのは専用のキーワード検索ツールだけで、それ以上に派手なものはありません。

パスも検索語になります。「session broker(セッション仲介)はどう動くの?」と尋ねると、AIはsession-brokersessionBrokersession_brokerの3つの綴りでファイル名を検索します。session-broker/というディレクトリは、最初の一行を読む前から、すでに一度ヒットしているのです。

AIはこのループを回っています。検索し、ヒット箇所の周辺を読み、足りなければ別の言葉で再検索し、十分になればようやく編集を始めます。

rgで検索 ヒット箇所を読む 足りた? 足りなければ単語変更 編集開始 足りなければ単語を変えてもう一周 名前が曖昧なほど、このループの周回数は増え、毎周ファイルを読む AIのナビゲーションループ AIのナビゲーションループ rgで検索 ヒット箇所を読む 足りた? 足りなければ単語変更 編集開始 もう一周 名前が曖昧なほど、このループの周回数は増え、 毎周ファイルを読む
AIがあなたのリポジトリ内で辿るナビゲーションループ(本サイト作成)

つまり、この連鎖の中に、コードを書く人間が完全にコントロールできる入力が一つあります。それは、あなたのコード内の語彙とファイル名が、優れた検索語になり得るかどうかです。

このコストを計算したのがModemです。この会社は2025年初頭に創業し、コードを全てAIで書くことを決定しました。1年間でTypeScriptアプリコード36万行、テストコード32万行を生成し、その99.9%をモデルが作成しました。以下の知見と1,680回の対照実験は、すべてこの1年間の成果です。

2 コスト

名前が曖昧だと、どれほどのコストになるのか?

では、「優れた検索語」とそうでない場合、具体的にどれだけ違うのでしょうか。彼らは3つの関数で比較しました。

これら3つの関数は機能が完全に同じで、どれも外部サービスを呼び出すAPIクライアントを作成します。違いは名前だけです。

同じ動作に対する3つの命名
export function create(apiKey: string) { ... }
export function createClient(apiKey: string) { ... }
export function createStripeClient(apiKey: string) { ... }

Modemの約2,900ファイルからなるTypeScriptリポジトリで、それぞれを検索してみます。

同じリポジトリ、3つの名前の検索ヒット数
create1,585行 · 459ファイル
createClient466行 · 23ファイル
createStripeClient43行 · 19ファイル
検索自体は3回とも40〜50ミリ秒で、速度に差はありません。バーの長さはヒット行数に基づいています。データはModemの自社リポジトリでの実測値。図は本サイト作成。

つまり問題は検索の速さではありません。grepが返すのはマッチした行であり、答えではありません。const client = create(config)という一行を見ても、それがAIに求められているクライアントなのか、それとも同じcreateという名前の数百あるうちの一つなのかは分かりません。

それを判断するには、ファイルを開いてヒット箇所の周辺を読み、さらに範囲を広げたり、別の箇所を開いたりする必要があります。ファイルは数十行から数千行まで様々で、1行あたり約10トークン(トークンはAIが読み書きするテキストの単位。一度の会話で扱える量や料金はこれで計算され、おおよそ英単語1つが1トークン強に相当)とすると、誤った候補を1つ排除するだけで数百〜数千トークンを消費します。それを十数個続ければ、数万トークンが消えていきます。お願いした作業はまだ一字も実行されていないのに。

あの1,585件のヒットは、数百の無関係なものに散らばっています。テスト用のダミーデータ、データベースへのレコード挿入、バックグラウンドタスクの登録などです。一方、createStripeClientの43件のヒットは、定義、呼び出し箇所、テストで、すべて同じクライアントについて語っています。