- コンテキストキャッシュで大規模コードベースの繰り返し処理コストを最大70%削減
- キャッシュの有効期間・更新戦略でプロジェクト単位の最適化が可能
- SES現場のコードレビュー・テスト生成でキャッシュを活用する実践パターン
「毎回同じプロジェクトのコードを読み込むのに大量のトークンを消費している…」——Gemini CLIを日常的に使っている開発者なら、この悩みに共感するのではないでしょうか。
Gemini CLIのコンテキストキャッシュ機能を使えば、大規模コードベースの繰り返し分析にかかるコストを最大70%削減できます。一度読み込んだプロジェクトのコンテキストをキャッシュし、後続のリクエストではキャッシュされた情報を再利用することで、トークン消費を大幅に抑制します。
この記事では、コンテキストキャッシュの仕組みから設定方法、SES現場での実践的な活用パターンまで解説します。

コンテキストキャッシュとは?
基本的な仕組み
Gemini APIのコンテキストキャッシュ(Context Caching)は、大量の入力トークンを事前にキャッシュし、後続のリクエストで再利用する仕組みです。
【キャッシュなし】
リクエスト1: [プロジェクト全体のコード] + [質問1] → 回答1 (100Kトークン)
リクエスト2: [プロジェクト全体のコード] + [質問2] → 回答2 (100Kトークン)
リクエスト3: [プロジェクト全体のコード] + [質問3] → 回答3 (100Kトークン)
→ 合計: 300Kトークン消費
【キャッシュあり】
キャッシュ作成: [プロジェクト全体のコード] → キャッシュID (100Kトークン、1回のみ)
リクエスト1: [キャッシュID] + [質問1] → 回答1 (キャッシュヒット)
リクエスト2: [キャッシュID] + [質問2] → 回答2 (キャッシュヒット)
リクエスト3: [キャッシュID] + [質問3] → 回答3 (キャッシュヒット)
→ 合計: 100K + キャッシュストレージ料金(大幅削減)
コスト比較
| 項目 | 通常料金 | キャッシュ利用時 | 削減率 |
|---|---|---|---|
| 入力トークン | $0.075/100万 | $0.01875/100万 | 75% |
| キャッシュストレージ | - | $1.00/100万トークン/時 | - |
| 出力トークン | $0.30/100万 | $0.30/100万 | 0% |
キャッシュが有効なケース:同じコンテキストに対して3回以上のリクエストを行う場合、キャッシュの方がコスト効率が良くなります。
対応モデル
| モデル | キャッシュ対応 | 最大キャッシュサイズ |
|---|---|---|
| Gemini 2.5 Pro | ✅ | 2Mトークン |
| Gemini 2.5 Flash | ✅ | 1Mトークン |
| Gemini 2.0 Flash | ✅ | 1Mトークン |
| Gemini 1.5 Pro | ✅ | 2Mトークン |
Gemini CLIでのキャッシュ活用
基本的な使い方
Gemini CLIでは、セッション内でコンテキストキャッシュを自動的に活用します:
# プロジェクトディレクトリで起動
cd /path/to/project
gemini
# 最初のリクエストでプロジェクトをキャッシュ
> @src このプロジェクトのアーキテクチャを説明して
# 2回目以降はキャッシュヒット(高速・低コスト)
> このプロジェクトのセキュリティリスクを分析して
> パフォーマンスのボトルネックを特定して
GEMINI.mdでのキャッシュ設定
プロジェクトのGEMINI.mdにキャッシュポリシーを定義:
## コンテキストキャッシュ設定
### キャッシュ対象
- src/ ディレクトリ全体
- package.json
- tsconfig.json
- prisma/schema.prisma
### キャッシュ除外
- node_modules/
- dist/
- coverage/
- *.test.ts(テストファイルはオンデマンド)
### キャッシュ戦略
- 有効期間: 1時間
- 更新トリガー: git commitごと
API直接利用でのキャッシュ制御
プログラマティックにキャッシュを管理する場合:
import { GoogleGenerativeAI } from '@google/generative-ai';
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
// キャッシュの作成
const cacheResult = await genAI.cacheManager.create({
model: 'gemini-2.5-pro',
contents: [
{
role: 'user',
parts: [
{ text: `以下はプロジェクトのソースコードです:\n${sourceCode}` }
],
},
],
ttlSeconds: 3600, // 1時間有効
displayName: 'my-project-context',
});
console.log(`キャッシュID: ${cacheResult.name}`);
console.log(`トークン数: ${cacheResult.usageMetadata.totalTokenCount}`);
// キャッシュを使用してリクエスト
const model = genAI.getGenerativeModelFromCachedContent(cacheResult);
const result = await model.generateContent('このコードのバグを見つけて');
console.log(result.response.text());
実践パターン1:大規模コードレビューの高速化
コードベース全体をキャッシュしたレビュー
SES現場で数十万行のコードベースをレビューする場合、毎回全コードを送信するのは非効率です:
// プロジェクトのソースコードを一括キャッシュ
async function createProjectCache(projectPath: string) {
const files = await glob(`${projectPath}/src/**/*.{ts,tsx}`, {
ignore: ['**/*.test.*', '**/*.spec.*']
});
const sourceCode = await Promise.all(
files.map(async f => {
const content = await fs.readFile(f, 'utf-8');
return `// File: ${path.relative(projectPath, f)}\n${content}`;
})
);
return await genAI.cacheManager.create({
model: 'gemini-2.5-pro',
contents: [{
role: 'user',
parts: [{ text: sourceCode.join('\n\n') }]
}],
ttlSeconds: 7200, // 2時間有効
displayName: `review-${path.basename(projectPath)}`,
});
}
// キャッシュを使って複数の観点でレビュー
const cache = await createProjectCache('/project');
const model = genAI.getGenerativeModelFromCachedContent(cache);
const reviews = await Promise.all([
model.generateContent('セキュリティの脆弱性を分析して'),
model.generateContent('パフォーマンスのボトルネックを特定して'),
model.generateContent('コーディング規約違反を検出して'),
model.generateContent('型安全性の問題を指摘して'),
]);
コスト効果
| 観点 | キャッシュなし | キャッシュあり | 削減額 |
|---|---|---|---|
| 4回のレビュー(200K入力) | $0.060 | $0.019 | $0.041 |
| 毎日1回×20営業日 | $1.200 | $0.380 | $0.820/月 |
| チーム5人×20営業日 | $6.000 | $1.900 | $4.100/月 |
実践パターン2:インクリメンタルなコード分析
差分ベースの効率的な分析
git diffで変更されたファイルのみを追加コンテキストとして送信し、キャッシュされたベースコンテキストと組み合わせる:
async function incrementalReview(cache: CachedContent, diffText: string) {
const model = genAI.getGenerativeModelFromCachedContent(cache);
const result = await model.generateContent(`
プロジェクトのコンテキストはキャッシュ済みです。
以下のgit diffの変更内容をレビューしてください:
${diffText}
特に以下の点を確認:
1. 既存コードとの整合性
2. テストの網羅性
3. パフォーマンスへの影響
`);
return result.response.text();
}
// git diffの取得とレビュー実行
const diff = execSync('git diff origin/main...HEAD').toString();
const review = await incrementalReview(projectCache, diff);
CI/CDパイプラインでのキャッシュ活用
# .github/workflows/gemini-review.yml
name: Gemini Cached Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: キャッシュIDの取得/作成
id: cache
run: |
# 既存キャッシュの確認
CACHE_ID=$(curl -s "https://generativelanguage.googleapis.com/v1beta/cachedContents?key=$GEMINI_API_KEY" \
| jq -r '.cachedContents[] | select(.displayName=="ci-review-cache") | .name')
if [ -z "$CACHE_ID" ]; then
echo "新規キャッシュ作成..."
# プロジェクトコードをキャッシュ
CACHE_ID=$(node scripts/create-cache.js)
fi
echo "cache_id=$CACHE_ID" >> $GITHUB_OUTPUT
- name: 差分レビュー
run: |
git diff origin/main...HEAD > /tmp/diff.txt
node scripts/cached-review.js \
--cache-id "${{ steps.cache.outputs.cache_id }}" \
--diff /tmp/diff.txt
実践パターン3:ドキュメント生成の効率化
プロジェクト全体を理解した上でのドキュメント生成
// キャッシュされたプロジェクトコンテキストから各種ドキュメントを生成
const cache = await getOrCreateProjectCache();
const model = genAI.getGenerativeModelFromCachedContent(cache);
// 複数のドキュメントを並列生成(全てキャッシュヒット)
const [apiDocs, archDocs, deployDocs, testDocs] = await Promise.all([
model.generateContent('REST APIのOpenAPI仕様書を生成して'),
model.generateContent('システムアーキテクチャ図(Mermaid形式)を生成して'),
model.generateContent('デプロイ手順書を生成して'),
model.generateContent('テスト仕様書を生成して'),
]);
キャッシュのライフサイクル管理
class CacheManager {
private cacheId: string | null = null;
private lastUpdate: Date | null = null;
async getOrCreate(projectPath: string): Promise<CachedContent> {
// キャッシュが存在し、有効期限内ならそのまま使用
if (this.cacheId && this.isValid()) {
return await genAI.cacheManager.get(this.cacheId);
}
// 新規作成または更新
const sourceCode = await this.loadProjectSource(projectPath);
const cache = await genAI.cacheManager.create({
model: 'gemini-2.5-pro',
contents: [{ role: 'user', parts: [{ text: sourceCode }] }],
ttlSeconds: 3600,
});
this.cacheId = cache.name;
this.lastUpdate = new Date();
return cache;
}
// キャッシュの更新(コード変更時)
async refresh(projectPath: string): Promise<void> {
if (this.cacheId) {
await genAI.cacheManager.delete(this.cacheId);
}
await this.getOrCreate(projectPath);
}
private isValid(): boolean {
if (!this.lastUpdate) return false;
const elapsed = Date.now() - this.lastUpdate.getTime();
return elapsed < 3600 * 1000; // 1時間以内
}
}
実践パターン4:チーム共有キャッシュ
チーム内でのキャッシュ共有
同じプロジェクトを複数のチームメンバーが分析する場合、キャッシュIDを共有してコスト削減:
// キャッシュIDをRedisで共有
import Redis from 'ioredis';
const redis = new Redis();
async function getSharedCache(projectName: string): Promise<string | null> {
return await redis.get(`gemini-cache:${projectName}`);
}
async function setSharedCache(projectName: string, cacheId: string): Promise<void> {
// TTLと合わせてRedisにも有効期限設定
await redis.setex(`gemini-cache:${projectName}`, 3600, cacheId);
}
// 使用例
let cacheId = await getSharedCache('my-project');
if (!cacheId) {
const cache = await createProjectCache('/project');
cacheId = cache.name;
await setSharedCache('my-project', cacheId);
}
キャッシュ最適化のベストプラクティス
何をキャッシュすべきか
| コンテンツ | キャッシュ推奨度 | 理由 |
|---|---|---|
| ソースコード(本体) | ⭐⭐⭐ | 頻繁に参照、変更頻度は中程度 |
| 型定義・インターフェース | ⭐⭐⭐ | 全ファイルから参照される |
| 設定ファイル | ⭐⭐ | 変更頻度が低い |
| テストファイル | ⭐ | オンデマンドで十分 |
| ドキュメント | ⭐⭐ | 参照は多いが変更少ない |
キャッシュサイズの最適化
// 大規模プロジェクトでのキャッシュサイズ制御
async function createOptimizedCache(projectPath: string) {
const files = await glob(`${projectPath}/src/**/*.ts`);
// ファイルサイズでソートし、上位ファイルを優先
const sortedFiles = files
.map(f => ({ path: f, size: fs.statSync(f).size }))
.sort((a, b) => b.size - a.size);
let totalTokens = 0;
const maxTokens = 1_000_000; // 1Mトークン上限
const selectedFiles: string[] = [];
for (const file of sortedFiles) {
const estimatedTokens = file.size / 4; // 概算: 4バイト/トークン
if (totalTokens + estimatedTokens > maxTokens) break;
selectedFiles.push(file.path);
totalTokens += estimatedTokens;
}
console.log(`キャッシュ対象: ${selectedFiles.length}ファイル (約${totalTokens}トークン)`);
// キャッシュ作成
const sourceCode = await Promise.all(
selectedFiles.map(async f => {
const content = await fs.promises.readFile(f, 'utf-8');
return `// ${path.relative(projectPath, f)}\n${content}`;
})
);
return genAI.cacheManager.create({
model: 'gemini-2.5-pro',
contents: [{ role: 'user', parts: [{ text: sourceCode.join('\n\n') }] }],
ttlSeconds: 3600,
});
}
TTL(有効期間)の最適設定
| 用途 | 推奨TTL | 理由 |
|---|---|---|
| 日次レビュー | 8時間 | 営業時間中有効 |
| CI/CDパイプライン | 1時間 | ビルドごとに更新 |
| ドキュメント生成 | 24時間 | 変更頻度が低い |
| 対話的な開発 | 2時間 | セッション中有効 |
SES現場での費用対効果
プロジェクト規模別のコスト削減効果
| プロジェクト規模 | 月間リクエスト数 | キャッシュなし | キャッシュあり | 月間削減額 |
|---|---|---|---|---|
| 小規模(10Kトークン) | 100回 | $0.75 | $0.28 | $0.47 |
| 中規模(100Kトークン) | 500回 | $37.50 | $11.25 | $26.25 |
| 大規模(500Kトークン) | 1000回 | $375.00 | $112.50 | $262.50 |
ROI計算
SES現場での開発者1人あたりの月間利用を想定:
- キャッシュ導入コスト: 約2時間の設定作業(1回のみ)
- 月間削減額: 中規模プロジェクトで約$26(約4,000円)
- チーム5人: 月間約$130(約20,000円)削減
- ROI: 初月で投資回収完了
トラブルシューティング
よくある問題と対処法
Q: キャッシュが期限切れになる
A: TTLを適切に設定し、自動更新の仕組みを導入してください:
// 自動更新機構
setInterval(async () => {
const cache = await genAI.cacheManager.get(cacheId);
if (cache.expireTime && new Date(cache.expireTime) < new Date(Date.now() + 10 * 60 * 1000)) {
// 10分以内に期限切れなら更新
await genAI.cacheManager.update(cacheId, { ttlSeconds: 3600 });
console.log('キャッシュのTTLを更新しました');
}
}, 5 * 60 * 1000); // 5分ごとにチェック
Q: キャッシュサイズの上限に達する
A: 重要度の低いファイルを除外し、コア部分のみをキャッシュしてください。.geminiignoreファイルで除外パターンを定義できます。
Q: キャッシュの内容が古い
A: git hookを使ってコミット時にキャッシュを自動更新:
# .git/hooks/post-commit
#!/bin/bash
node scripts/refresh-gemini-cache.js &
まとめ:コンテキストキャッシュでAI開発コストを最適化
Gemini CLIのコンテキストキャッシュは、日常的にAIを活用する開発者にとってコスト削減の決定打です。
導入チェックリスト
- ✅ プロジェクトのソースコード量を確認(32K以上ならキャッシュ有効)
- ✅ GEMINI.mdにキャッシュ対象を定義
- ✅ TTLをユースケースに合わせて設定
- ✅ CI/CDパイプラインにキャッシュ管理を統合
- ✅ チーム内でキャッシュIDを共有する仕組みを構築
SES現場では、AI活用のコスト管理スキルが重視されています。コンテキストキャッシュを適切に運用できるエンジニアは、プロジェクトのコスト最適化に貢献できる人材として評価されます。
SES BASEでは、Gemini / Google Cloud関連の案件を多数掲載しています。 AI開発案件を探す で最新の案件をチェックしてください。