Gemini CLI や Code Assist を本気で使い始めると、最初に直面するのが「関係ないファイルまで読み込まれて回答がぼやける」問題ではないでしょうか。私も最近、自社プロダクトのモノレポに Code Assist を入れた直後、node_modules の型定義を律儀に拾いに行って延々とトークンを消費する挙動に頭を抱えました。
この問題を解くための公式機能が .geminiignore です。.gitignore と書式は同じですが、役割は「Git に含めない」ではなく「Gemini に見せない」。ここでは見落とされがちなこのファイルの実践的な使い方を、私が実際の開発現場で詰まったポイントと一緒に整理していきます。
.geminiignore とは何か — .gitignore との違い
.geminiignore は、リポジトリのルートに置くテキストファイルで、Gemini CLI と Code Assist がコンテキストを構築するときに「読み込まないファイル/ディレクトリ」を指定します。書式は .gitignore と互換で、行単位のグロブパターン、! による除外解除、# のコメントが使えます。
似た用途の .gitignore と混同されがちですが、目的が違います。
.gitignore: バージョン管理に含めない(コミットしない).geminiignore: AI に読ませない(コンテキストに乗せない)
たとえば、ビルド済みの dist/ は Git に含めたいが、Code Assist には不要という現場は珍しくありません。逆に、テスト用のスナップショットは Git に含めないものの、AI には読ませたいケースもあります。両者は別軸の設定なので、.gitignore だけで済ませようとすると必ずどこかで破綻します。
公式ドキュメントには触れられていませんが、私が試した範囲では .geminiignore は プロジェクト直下に1つだけ 配置するのが安定動作します。サブディレクトリに置いた場合は無視される、もしくは挙動が一貫しないことがありました。
なぜコンテキスト整理が回答精度を左右するのか
Gemini 3 系のモデルは 100 万〜200 万トークンの長大なコンテキスト窓を持ちますが、入力が長いほど回答品質が落ちる傾向があるのは、私が試した実装でも一貫して観測されています。これは「Lost in the Middle」と呼ばれる現象で、入力中盤の情報がモデルの注意から漏れやすくなる挙動です。
.geminiignore を整備する目的は3つあります。
- 精度の改善: 不要なファイルを排除することで、モデルが本当に重要なコードに集中できる
- コストの削減: 読み込まれるトークンが減るため、API 課金や月次のトークン枠を圧迫しにくくなる
- 応答速度の向上: 入力が短くなるほど、特に Flash 系モデルでは応答が体感で速くなる
私のプロジェクトでは、無設定の状態だと Code Assist の Agent Mode が package-lock.json を毎回ロードしようとして、それだけで30万トークン近く消費していました。.geminiignore で除外した直後、同じタスクが10分の1のトークンで完結するようになり、月末の請求も明確に下がりました。
最低限入れておきたいパターン集
以下は、汎用的なプロジェクトで真っ先に除外すべきパターンです。私が複数のプロジェクトで実運用してきた結果、ほぼどんなスタックでも問題が出にくい組み合わせです。
# .geminiignore — Generic baseline
# 依存関係
node_modules/
.pnpm-store/
vendor/
.venv/
__pycache__/
# ビルド成果物
dist/
build/
.next/
out/
target/
*.min.js
*.bundle.js
# ロックファイル(巨大かつコンテキストとして無価値)
package-lock.json
pnpm-lock.yaml
yarn.lock
poetry.lock
Cargo.lock
# IDE / OS
.idea/
.vscode/settings.json
.DS_Store
# 機密情報(最重要)
.env
.env.*
!.env.example
*.pem
*.key
secrets/
# 大きなアセット
*.mp4
*.zip
*.tar.gz
public/uploads/機密情報の項目は最優先で書いてください。.env を Gemini に流してしまうと、生成されたコードや回答に API キーが混入することがあります。.gitignore で守れていても、.geminiignore でも明示的に除外しておくのが二重防御として正解です。
モノレポでの応用パターン
私が最近関わったモノレポでは、apps/web と apps/admin の2つを同時に開いていると、片方の作業中にもう片方のコードが雑音として入り込む問題がありました。.geminiignore を以下のように構成すると、用途別にコンテキストを切り替えられます。
# .geminiignore — Monorepo
# 共通除外
node_modules/
dist/
.turbo/
.next/
# 一時的に admin の作業に集中したい場合(コメントアウトで切り替え)
# apps/web/
# packages/ui のテストは精度に効くので除外しない(!で復活させる)
packages/ui/dist/
!packages/ui/src/__tests__/ここでのポイントは、! で除外解除を使えばす。packages/ui/dist/ は除外しつつ、__tests__/ は読ませる、というように粒度を細かく制御できます。テストコードは API の使い方をモデルに教える教材として優秀なので、私は基本的にテストを除外しません。
ブランチ単位で .geminiignore を切り替えるテクニックも有効です。feature/admin-refactor ブランチでは apps/web/ を除外し、feature/web-redesign では逆にする、という運用にしておくと、AI が常に「いま触っている領域」だけに集中してくれます。
トークン消費を計測して効果を確かめる
.geminiignore の効果は、設定前後でトークン消費を比較しないと体感だけになりがちです。Gemini API では、countTokens を使うとコンテキストに含まれるトークン数を実行前に確認できます。簡単な検証スクリプトを置いておきます。
# context_token_check.py
# .geminiignore を変更したときに、コンテキストのトークン消費がどう変わるかを確認する
import os
import pathlib
import fnmatch
from google import genai
# .geminiignore を読み込んでパターン化
def load_ignore_patterns(root: pathlib.Path) -> list[str]:
ignore_file = root / ".geminiignore"
if not ignore_file.exists():
return []
return [
line.strip()
for line in ignore_file.read_text().splitlines()
if line.strip() and not line.startswith("#")
]
def is_ignored(path: pathlib.Path, patterns: list[str]) -> bool:
rel = str(path)
for pat in patterns:
if fnmatch.fnmatch(rel, pat) or rel.startswith(pat.rstrip("/")):
return True
return False
def collect_context(root: pathlib.Path, patterns: list[str]) -> str:
chunks = []
for path in root.rglob("*"):
if path.is_file() and not is_ignored(path.relative_to(root), patterns):
try:
chunks.append(path.read_text(errors="ignore"))
except Exception:
continue
return "\n".join(chunks)
if __name__ == "__main__":
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
root = pathlib.Path(".")
patterns = load_ignore_patterns(root)
context = collect_context(root, patterns)
# countTokens は実際の課金トークン数を返す
result = client.models.count_tokens(
model="gemini-2.5-flash",
contents=context,
)
print(f"Total tokens: {result.total_tokens:,}")
print(f"Patterns active: {len(patterns)}")期待される出力は次のようになります。
Total tokens: 38,420
Patterns active: 14
この数値を .geminiignore 編集前後で比較すると、どのパターンがコスト削減に効いているかが定量的に分かります。私のプロジェクトでは、package-lock.json を1行追加するだけで30万トークン → 4万トークンに減ったケースがありました。
よくあるつまずきと対処
.geminiignore を書いたのに効いていない、という相談を何度か受けたので、私が実際に踏んだ落とし穴を共有します。
ひとつ目は、ファイルの配置場所です。リポジトリのルート(.git ディレクトリと同じ階層)に置く必要があります。サブディレクトリに置くとツールによっては読み込まれません。配置を間違えても警告が出ない仕様なので、効いていないと感じたらまずここを疑ってください。
ふたつ目は、エディタの再起動が必要なことです。Code Assist の VS Code 拡張は .geminiignore を起動時にしか読み込まないため、ファイルを変更したら必ずエディタを再起動してください。Gemini CLI の場合は次の起動時から反映されます。
みっつ目は、! の順序依存です。.gitignore 同様、後から書いたパターンが優先されるので、除外解除(!pattern)は必ず除外パターンの後ろに書きます。順序を逆にすると、除外解除が効きません。
関連する設定ファイルとの使い分け
Gemini エコシステムには似た役割のファイルが複数あるので、整理しておきます。
.geminiignore: コンテキストから除外するファイルを指定gemini.md/GEMINI.md: プロジェクトの説明やコーディング規約をモデルに伝える指示書.gemini/config.yaml: Gemini CLI の各種設定(モデル、出力形式など)
これらは独立して機能します。.geminiignore で除外を、gemini.md で指示を、config.yaml で挙動を、それぞれ役割分担させるのが基本です。gemini.md の書き方については GEMINI.md セットアップガイド で詳しく扱っているので、合わせて整備すると効果が倍になります。
CLI 側の使い方そのものに不安が残る場合は、Gemini CLI 完全ガイド と Gemini Code Assist の Agent Mode 実践ガイド も合わせて読むと、.geminiignore の効果がより腑に落ちます。
書籍で体系的に
まずは1ファイル足してみる
ここまで読んでいただいた方には、ぜひ今日のうちに .geminiignore を1行だけでも追加して試してほしいです。node_modules/ と package-lock.json を書くだけでも、Code Assist の体感速度と回答精度が変わります。
整備するときの順番としては、機密情報(.env*)→ 巨大ファイル(ロックファイル・ビルド成果物)→ ノイズになる依存関係(node_modules/、vendor/)の順に進めるのが安全です。完璧を目指さず、まずは小さく始めて、トークン消費の変化を見ながら少しずつ調整していきましょう。