「認証まわりって、どこでやってる?」と一言頼んだだけで、エージェントが30個近いファイルを開き、丁寧な要約を作り、残りコンテキストが2割を切る。しかも答えはまだ出ていない。
原因はモデルの賢さでも、指示文の書き方でもないことが多い。調べ方が「全文を渡す」に固定されているのだ。grep で当たりを付けて、ヒットしたファイルを丸ごと読む。人間がやる分には数秒の手癖だが、エージェントがやると読んだ分だけ有限の窓が埋まる(読解力ではなく積載量の問題)。
この記事は、全文を渡す前に用途ごとの抽出系コマンドで範囲を絞る、という設計の話をする。特定のエージェント専用の話ではないので、手元のリポジトリでそのまま試せる。
この記事で扱うこと
- 「grep してファイル全読み」が、速度だけでなく精度まで下げる3つの理由
- 用途 → 使うコマンド → コンテキストに載せずに済むもの、の対応表
- 道具を入れただけでは使われない理由と、ルーティングをルール化する方法
前提
コマンドはエージェントの種類を問わない。Claude Code(Pro $20/月、Max $100〜200/月)でも、Cursor(Pro $20/月)でも、Codex CLI(ChatGPT サブスクに紐づく)でも、やることは同じだ。ツールごとの料金や特性の比較はAIコーディングツールを7つ比較した記事にまとめてある。
なぜ「grep して全文を読ませる」ではダメなのか
道具の話に入る前に、理屈を通しておきたい。ここが腹落ちしていないと、コマンドを覚えても結局いつもの手癖に戻る。
1. 無関係な行が増えるほど、後段の推論が鈍る
コンテキストウィンドウは有限で、しかも「入れておけば読んでくれる」ものではない。長いファイルを丸ごと渡しても、答えに効くのはたいてい一部の行だけで、残りはノイズとして推論に干渉する。エージェントが後半で急に的外れなことを言い出すとき、指示が悪いのではなく、判断材料が薄まっていることがある(濃度が下がると答えもぼやける)。
窓が埋まれば圧縮が走り、圧縮が走れば序盤の前提が丸められる。入れる量を減らすことは、そのまま精度の話でもある。
2. テキスト一致では、コメントと文字列リテラルを弾けない
「この関数の呼び出し箇所を全部挙げて」と頼むと、行単位の検索は次のものを同じ重みで拾う。
- コメントアウトされた古い呼び出し
- ドキュメント内のサンプルコード
- エラーメッセージやログ文字列に含まれる同名の単語
人間なら目視で弾くが、エージェントは検索結果を根拠に「12箇所で使われています」と報告する。実際は7箇所、というのはよくある。網羅的な調査のはずが、コメントアウトされた過去の遺産まで現役として数えているわけだ。ここで厄介なのは、間違っているのに自信ありげに正しい形式で返ってくることだ。
3. 構造化データは、全文がなくても答えが出る
JSON・YAML・CSV は、そもそも全文を読む必要がない。知りたいのは特定のキーか特定の行であって、残りは構造の飾りだ。数万行のロックファイルや集計用CSVをそのまま貼り付けるのは、辞書を1冊渡して「この単語の意味を教えて」と言うのに近い。
用途 → コマンド → 載せずに済むもの
これが記事の主張そのものなので、先に一覧で置いておく。
| 調べたいこと | 使うコマンド | コンテキストに載せずに済むもの |
|---|---|---|
| コードベースの概観(言語構成・規模) | tokei | ディレクトリ走査とファイル一覧の全出力 |
| 呼び出し箇所・宣言の正確な洗い出し | ast-grep --pattern | コメント・文字列リテラルの偽陽性ヒット |
| 同一パターンを3箇所以上書き換える | ast-grep --pattern … --rewrite … | 書き換え対象ファイルの全文 |
| 構造が未知のJSONの探索 | gron <file> | rg <keyword> | 数千行のJSON本体 |
| JSONの特定キーだけ取得 | jq | 同上 |
| YAML/TOML/XML の一部だけ取得 | yq | 設定ファイル全文 |
| CSV・Parquet・巨大JSONの集計 | duckdb -c "SELECT ..." | データ行そのもの |
| PDF・docx・zip・sqlite の中の検索 | rga | 変換済み全文テキスト |
| 機械的な文字列置換 | sd | 置換対象ファイルの全文 |
| 整形後に挙動が変わっていないかの確認 | difft --exit-code | diff 全体の目視レビュー |
| 速い/遅いの裏取り | hyperfine | 体感ベースの推測 |
判断の流れにすると、こうなる。
以下、章ごとに主役を1つずつ見ていく。
全体像は1コマンドで取る
知らないリポジトリに入ったエージェントが最初にやりがちなのは、ディレクトリを片っ端から開いて構造を把握することだ。やっていること自体は丁寧なのに、概観を得るための手段としては高すぎる(正直、丁寧さの代金をコンテキストで払っているだけだ)。
tokei は言語構成と行数を1発で出す。
tokei .
出力は数十行に収まる。「TypeScript が8割、テストが2割、Python は補助スクリプトだけ」といった判断が、ファイルを1つも開かずに付く。私たちが自分の開発環境の設定に書いているルーティング表でも、この項目は「ファイルを読み始める前にまず全体像」という位置づけにしてある。
エージェントに調査を頼むときも、先にこの出力を渡しておくだけで探索の当てずっぽうが目に見えて減る。
「見つけた」を偽陽性から守る
先ほどの理屈の2番目、コメントと文字列リテラルの問題への答えがこれだ。
ast-grep は構文木のノードに対してマッチするので、コメント内の同名トークンも、ログ文字列の中の同じ単語もヒットしない。
ast-grep --pattern 'fetchUser($$$)' --lang ts
$$$ は「引数が何個でも」を表すワイルドカードだ。返ってくるのは実際の呼び出し式だけなので、そのまま件数を根拠にできる。
一括書き換えも同じパターン記述で通る。
ast-grep --pattern 'oldApi($A)' --rewrite 'newApi($A)' --lang ts --update-all
導入理由として書き残してあるのは「構文認識の検索・一括書き換え (codemod)。AI agent がコンテキスト外で安全に一括変更するための道具」という一文だ。ここが要点で、書き換え対象の全文をエージェントに読ませずに、全箇所を機械的に網羅できる。rename や API 移行のように同じ形の変更が3箇所以上あるなら、1ファイルずつ開いて手で直させるより事故が少ない。
なお、構文を意識する必要がない単純な文字列置換なら sd で十分だ。道具は用途に対して選ぶ。
構造が未知のJSONを全読みしない
キー名が分かっていれば jq '.scripts' package.json で終わる。問題は「どこに何が入っているか分からない」ときで、ここで人は全文を開きたくなる。
gron は JSON を1行1パスの平坦な形式に展開する。行になれば行検索が効く。
gron package-lock.json | rg 'esbuild'
返ってくるのは json.packages["node_modules/esbuild"].version = "0.21.5"; のような行だけだ。パスが分かったら、そのパスを jq で取りに行けばいい。探索と取得を分けると、どちらの段階でも全文が要らなくなる。
設定ファイル一式についても同じ発想で、YAML/TOML/XML は yq で必要部分だけを抜く。手元のワークフロー定義から使っているアクションだけ知りたいなら、こう書ける。
yq '.jobs.build.steps[].uses' .github/workflows/ci.yml
データファイルは、必要な行だけ取り出す
CSV や Parquet、巨大な JSON をエージェントに渡すのは、たいてい集計結果が欲しいからだ。それなら集計してから渡せばいい。
duckdb はファイルを直接 SQL の対象にできる。
duckdb -c "SELECT status, count(*) FROM 'access.csv' GROUP BY 1 ORDER BY 2 DESC"
数十万行のファイルが、渡すときには数行の集計表になる。導入時のメモも「AI agent がデータファイルを全読みせず必要行だけ取り出す」と、ほぼそのままの目的で書いてある。
そもそもテキスト検索が効かない形式もある。PDF・docx・zip・sqlite の中を探したいときは rga(ripgrep-all)が中身を展開しながら検索してくれる。仕様書PDFの束から該当箇所だけ拾って渡す、といった使い方になる。
エージェントの報告を検算する
絞り込みと同じくらい大事なのが、返ってきた報告を安く検証することだ。
整形やリファクタの後で「挙動は変えていません」と言われたとき、目視で diff を追うのは高い。difftastic は構文木ベースで差分を取るので、フォーマットだけの変更なら構造差分ゼロとして扱える。
difft --exit-code before.ts after.ts
終了コードで判定できるのがポイントで、「構造変化ゼロ=フォーマットのみ、を機械判定」できる。レビューを1段まるごと省ける場面がある。
もう一つ、「速くなりました」という報告も要注意だ。体感や推測で速い/遅いを言わせないために、性能主張は hyperfine で計測する。導入理由にも「性能主張を計測で裏付ける」と書いてある。エビデンスを添える手間が1コマンドなら、添えない理由がない。
道具を入れただけでは、使われない
ここが実はいちばん大事な話だ。
抽出系のコマンドを一式インストールしても、エージェントの既定の手癖は変わらない。学習データの中で圧倒的に多いのは「grep して cat」だからだ。パッケージを足した時点では、エージェントはその存在を知らないし、知ったところで使う理由を持たない。道具の追加と、使い分けの明文化は別作業だと割り切ったほうがいい。
やることはシンプルで、エージェントが毎回読む指示ファイル(プロジェクト直下の指示メモリなど)に、先ほどの対応表を短く書き置く。書き出しはこの一文で足りる。
ファイル全読み・テキスト grep の前に専用 CLI で絞る
そのうえで「用途 → コマンド」を箇条書きで並べる。一般に、こういうルールは判断基準ではなく分岐条件の形で書いたほうが守られる。「なるべく絞る」ではなく「JSON なら jq、構造が未知なら gron に流す」と書く。
パッケージ一覧の側にも、名前から用途が読み取りにくいコマンドには「なぜ入れているか」を1行だけ添えておく。半年後の自分と、初見のエージェントの両方が読む場所だからだ。ここが空欄だと、次に環境を触るときに「これ何のために入れたんだっけ」から始まる(そして高確率で消される。さようなら、選び抜いた道具たち)。
CLI を手に馴染ませる話としては、シェルエイリアスでCLI操作を短縮した記事も同じ発想でまとめている。頻繁に打つものほど、打つ手間を先に潰しておく。
減らす側と、飛んだ後を救う側
この記事は「そもそもコンテキストの消費を減らす」話だ。それでも長い作業では窓が埋まり、圧縮が走る。消費を減らす設計と、圧縮された後に作業状態を復元する設計は、別々に必要になる。後者についてはコンテキスト圧縮のあとも作業を止めない方法にまとめてあるので、あわせて手当てしておくと事故が減る。
順番としては、まず入れる量を絞る。それでも溢れる分を、状態管理で受け止める。逆順にすると、復元しても中身がノイズだらけという状態が再生産される。
まとめ
エージェントにコードベースを調べさせるときの原則は、1行で言える。渡す前に絞る。
- 全文投入はコンテキストを食うだけでなく、無関係な行で推論を鈍らせる
- テキスト一致はコメントと文字列リテラルを弾けないので、構文ノードで検索する
- 構造化データは、集計や抽出を済ませてから渡す
- 道具を入れたら、指示ファイルに「用途 → コマンド」を書いて初めてルーティングされる
まずは tokei . を1回打ってみるところからでいい。手元のリポジトリの全体像が数十行で返ってきた時点で、「これ、エージェントに毎回ディレクトリを歩かせていたな」と気づけるはずだ。



