mcpField
G
google-gemini-file-search

google-gemini-file-search

github.com/ovachieverドキュメント5649 回インストール48 週間前に更新
リポジトリ

google-gemini-file-search について

Google Gemini File Searchをアプリに組み込む開発者向けの設定・実装スキルです。複数のデプロイ方法に対応するSDKの例に加え、インデックス作成、チャンク分割、費用、よくあるエラーを解説します。

主な機能

  1. 01@google/genaiの設定ガイド
  2. 02Node.js、Cloudflare Workers、Next.jsのテンプレート
  3. 03チャンク分割とメタデータの指針
  4. 04よくあるエラーの予防
  5. 05インデックス作成費用の最適化

活用シーン

  1. 01サポート資料のドキュメント検索を設定
  2. 02社内ドキュメント検索の構築
  3. 03法律・研究・製品文書の検索
  4. 04コード文書やAPIリファレンスの検索

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/ovachiever/droid-tings --skill google-gemini-file-search

インストール後、Claude Code で/google-gemini-file-searchを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

LICENSE
PROJECT_STATUS.md
README.md
.claude-plugin/
plugin.json
references/
README.md
scripts/
create-store.ts
README.md
templates/
README.md

詳細

Google Gemini File Search のセットアップ

概要

Google Gemini File Search は、個別のベクトルデータベース、独自のチャンク分割ロジック、埋め込み生成コードを不要にする、フルマネージドの RAG(検索拡張生成)システムです。ドキュメント(PDF、Word、Excel、コードファイルなど)をアップロードし、自然言語で検索できます。Gemini が、最適化されたモデルを使ったインテリジェントなチャンク分割、埋め込み生成、セマンティック検索、引用の生成を自動的に処理します。

このスキルでできること:

  • @google/genai File Search API の完全なセットアップガイド
  • TypeScript/JavaScript SDK の設定パターン
  • 3 つのデプロイシナリオ(Node.js、Cloudflare Workers、Next.js)に対応した動作するテンプレート
  • よくある 8 つのエラーと予防策の解説
  • 検索精度を最適化するためのチャンク分割のベストプラクティス
  • コスト最適化の手法
  • 比較ガイド(Cloudflare Vectorize、OpenAI Files API、Claude MCP との比較)

File Search の主な機能:

  • 100 種類以上のファイル形式:PDF、Word(.docx)、Excel(.xlsx)、PowerPoint(.pptx)、Markdown、JSON、CSV、コードファイル(Python、JavaScript、TypeScript、Java、C++、Go、Rust など)
  • 自動埋め込み生成:Google の Gemini Embedding モデルを使用します(独自の埋め込み生成コードは不要です)
  • セマンティック検索:ベクトルベースの検索により、キーワードだけでなく意味や文脈も理解します
  • 引用機能を内蔵:グラウンディングメタデータが、ドキュメント内の該当箇所を自動的に示します
  • カスタムメタデータ:ドキュメントごとに最大 20 個のカスタムキーと値のペアで検索クエリを絞り込めます
  • チャンク分割を設定可能:チャンクサイズ(トークン数)とオーバーラップを調整して、精度を最適化できます
  • コスト効率に優れる:インデックス作成は 100 万トークンあたり 0.15 ドル(1 回限り)、ストレージは無料(上限あり)、クエリ時の埋め込み生成も無料です

このスキルを使う場面

最適なユースケース:

  • カスタマーサポート用ナレッジベースの構築(マニュアル、FAQ、トラブルシューティングガイド)
  • 社内ドキュメント検索の作成(社内 Wiki、ポリシー、手順書)
  • 法務・コンプライアンス文書の分析(契約書、規制、判例法)
  • 研究ツール(学術論文、記事、教科書)
  • コードドキュメントの検索(API ドキュメント、SDK リファレンス、サンプル)
  • 製品情報の検索(仕様書、データシート、ユーザーガイド)

次の場合は File Search を使用してください:

  • ✅ フルマネージドの RAG ソリューションを使いたい場合(ベクトルデータベースのセットアップは不要)
  • ✅ コストの予測しやすさが重要な場合(継続的なストレージ料金ではなく、インデックス作成ごとに課金)
  • ✅ 幅広いファイル形式に対応する必要がある場合(100 種類以上を標準でサポート)
  • ✅ 引用が重要な場合(グラウンディングメタデータを内蔵)
  • ✅ シンプルなデプロイを優先する場合(API を 1 つセットアップするだけ)
  • ✅ ドキュメントが比較的静的な場合(更新頻度が低い)

次の場合は Cloudflare Vectorize/AutoRAG を使用してください:

  • ✅ グローバルエッジでのパフォーマンスが重要な場合(世界中で低レイテンシ)
  • ✅ Cloudflare 上でフルスタックアプリを構築する場合(Workers、R2、D1)
  • ✅ カスタムの埋め込みモデルや検索ロジックが必要な場合
  • ✅ R2 や外部ソースからリアルタイムでデータを更新する必要がある場合

次の場合は OpenAI Files API を使用してください:

  • ✅ すでに OpenAI Assistants API(会話スレッド)を使用している場合
  • ✅ 永続的なアシスタントのスレッドにナレッジを添付する必要がある場合
  • ✅ 非常に大規模なファイルコレクション(ストアごとに 10,000 ファイル以上)を扱う場合
  • ✅ ストレージベースの料金モデル(1 GB あたり 1 日 0.10 ドル)を希望する場合

このスキルを使わない場面

次の場合はこのスキルを使用しないでください:

  • ❌ カスタムの埋め込みモデルが必要(File Search では Gemini Embeddings に固定されます)
  • ❌ ドキュメントが頻繁に更新される(ストリーミング更新には対応しておらず、削除して再アップロードする必要があります)
  • ❌ 会話型 AI エージェントを構築する(代わりに OpenAI Assistants または Claude MCP を使用してください)
  • ❌ BM25/ハイブリッド検索が必要(File Search はベクトル検索のみです)
  • ❌ 高度な再ランキング設定が必要(自動設定のみです)
  • ❌ PDF 内の画像や表を解析する必要がある(テキスト抽出のみです)

前提条件

1. Google AI API キー

https://aistudio.google.com/apikey で API キーを作成します。

無料枠の上限:

  • 1 GB のストレージ(すべてのファイル検索ストアの合計)
  • 1 日あたり 1,500 リクエスト
  • 1 分あたり 100 万トークン

有料枠の料金:

  • インデックス作成:入力トークン 100 万件あたり $0.15(1 回限り)
  • ストレージ:無料(Tier 1:10 GB、Tier 2:100 GB、Tier 3:1 TB)
  • クエリ時の埋め込み:無料(取得したコンテキストは入力トークンとしてカウントされます)
2. Node.js 環境

最小バージョン: Node.js 18 以降(v20 以降を推奨)

node --version  # Should be >=18.0.0
3. @google/genai SDK をインストール
npm install @google/genai
# or
pnpm add @google/genai
# or
yarn add @google/genai

現在の安定版: 0.21.0 以降(npm view @google/genai version で確認してください)

4. TypeScript の設定(任意ですが推奨)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  }
}

よくあるエラーの防止

このスキルでは、File Search の実装時によく発生する 8 つのエラーを防止します。

エラー 1:ドキュメントの不変性

症状:

Error: Documents cannot be modified after indexing

原因: ドキュメントはインデックス作成後に変更できません。PATCH または UPDATE 操作はありません。

防止策: 更新には、削除して再アップロードするパターンを使用します。

// ❌ WRONG: Trying to update document (no such API)
await ai.fileSearchStores.documents.update({
  name: documentName,
  customMetadata: { version: '2.0' }
})

// ✅ CORRECT: Delete then re-upload
const docs = await ai.fileSearchStores.documents.list({
  parent: fileStore.name
})

const oldDoc = docs.documents.find(d => d.displayName === 'manual.pdf')
if (oldDoc) {
  await ai.fileSearchStores.documents.delete({
    name: oldDoc.name,
    force: true
  })
}

await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('manual-v2.pdf'),
  config: { displayName: 'manual.pdf' }
})

出典: https://ai.google.dev/api/file-search/documents

エラー 2:ストレージのクォータ超過

症状:

Error: Quota exceeded. Expected 1GB limit, but 3.2GB used.

原因: ストレージの計算には、入力ファイル、埋め込み、メタデータが含まれます。ストレージの合計は入力サイズのおよそ 3 倍です。

防止策: アップロード前にストレージ容量を計算します。

// ❌ WRONG: Assuming storage = file size
const fileSize = fs.statSync('data.pdf').size // 500 MB
// Expect 500 MB usage → WRONG

// ✅ CORRECT: Account for 3x multiplier
const fileSize = fs.statSync('data.pdf').size // 500 MB
const estimatedStorage = fileSize * 3 // 1.5 GB (embeddings + metadata)
console.log(`Estimated storage: ${estimatedStorage / 1e9} GB`)

// Check if within quota before upload
if (estimatedStorage > 1e9) {
  console.warn('⚠️ File may exceed free tier 1 GB limit')
}

出典: https://blog.google/technology/developers/file-search-gemini-api/

エラー 3:チャンク分割設定の誤り

症状: 検索品質の低下、無関係な結果、または文の途中でコンテキストが途切れる。

原因: デフォルトのチャンク分割設定は、コンテンツの種類に最適とは限りません。

防止策: 推奨されるチャンク分割方式を使用します。

// ❌ WRONG: Using defaults without testing
await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('docs.pdf')
  // Default chunking may be too large or too small
})

// ✅ CORRECT: Configure chunking for precision
await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('docs.pdf'),
  config: {
    chunkingConfig: {
      whiteSpaceConfig: {
        maxTokensPerChunk: 500,  // Smaller chunks = more precise retrieval
        maxOverlapTokens: 50     // 10% overlap prevents context loss
      }
    }
  }
})

チャンク分割のガイドライン:

  • 技術文書/コード: 1 チャンクあたり 500 トークン、オーバーラップ 50 トークン
  • 文章/記事: 1 チャンクあたり 800 トークン、オーバーラップ 80 トークン
  • 法律文書/契約書: 1 チャンクあたり 300 トークン、オーバーラップ 30 トークン(高精度)

出典: https://www.philschmid.de/gemini-file-search-javascript

エラー 4:メタデータの上限超過

症状:

Error: Maximum 20 custom metadata key-value pairs allowed

原因: 各ドキュメントに設定できるメタデータフィールドは最大 20 個です。

防止策: 簡潔なメタデータスキーマを設計します。

// ❌ WRONG: Too many metadata fields
await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('doc.pdf'),
  config: {
    customMetadata: {
      doc_type: 'manual',
      version: '1.0',
      author: 'John Doe',
      department: 'Engineering',
      created_date: '2025-01-01',
      // ... 18 more fields → Error!
    }
  }
})

// ✅ CORRECT: Use hierarchical keys or JSON strings
await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('doc.pdf'),
  config: {
    customMetadata: {
      doc_type: 'manual',
      version: '1.0',
      author_dept: 'John Doe|Engineering',  // Combine related fields
      dates: JSON.stringify({                // Or use JSON for complex data
        created: '2025-01-01',
        updated: '2025-01-15'
      })
    }
  }
})

出典: https://ai.google.dev/api/file-search/documents

エラー 5:インデックス作成コストの予想外の請求

症状: ドキュメントを 10 GB アップロードした後に、予想外の $375 の請求が発生する。

原因: インデックス作成の費用は 1 回限りですが、入力トークン数に基づいて計算されます(トークン 100 万件あたり $0.15)。

防止策: インデックス作成前に費用を見積もります。

// ❌ WRONG: No cost estimation
await uploadAllDocuments(fileStore.name, './data') // 10 GB uploaded → $375 surprise

// ✅ CORRECT: Calculate costs upfront
const totalSize = getTotalDirectorySize('./data') // 10 GB
const estimatedTokens = (totalSize / 4) // Rough estimate: 1 token ≈ 4 bytes
const indexingCost = (estimatedTokens / 1e6) * 0.15

console.log(`Estimated indexing cost: $${indexingCost.toFixed(2)}`)
console.log(`Estimated storage: ${(totalSize * 3) / 1e9} GB`)

// Confirm before proceeding
const proceed = await confirm(`Proceed with indexing? Cost: $${indexingCost.toFixed(2)}`)
if (proceed) {
  await uploadAllDocuments(fileStore.name, './data')
}

費用の例:

  • テキスト 1 GB ≈ 2 億 5,000 万トークン = インデックス作成費用 $37.50
  • PDF 100 MB ≈ 2,500 万トークン = インデックス作成費用 $3.75
  • コード 10 MB ≈ 250 万トークン = インデックス作成費用 $0.38

出典: https://ai.google.dev/pricing

エラー 6:オペレーションのステータスをポーリングしない

症状: アップロード直後のクエリで結果が返らない、またはインデックス作成が不完全になる。

原因: ファイルのアップロードは非同期で処理されます。done: true になるまでオペレーションをポーリングする必要があります。

防止策: 必ずオペレーションのステータスをポーリングします。

// ❌ WRONG: Assuming upload is instant
const operation = await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('large.pdf')
})
// Immediately query → No results!

// ✅ CORRECT: Poll until indexing complete
const operation = await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('large.pdf')
})

// Poll every 1 second
while (!operation.done) {
  await new Promise(resolve => setTimeout(resolve, 1000))
  operation = await ai.operations.get({ name: operation.name })
  console.log(`Indexing progress: ${operation.metadata?.progress || 'processing...'}`)
}

if (operation.error) {
  throw new Error(`Indexing failed: ${operation.error.message}`)
}

console.log('✅ Indexing complete:', operation.response.displayName)

出典: https://ai.google.dev/api/file-search/file-search-stores#uploadtofilesearchstore

エラー 7:強制削除を忘れる

症状:

Error: Cannot delete store with documents. Set force=true.

原因: ドキュメントがあるストアを削除するには force: true が必要です(誤削除を防ぐため)。

防止策: 空ではないストアを削除するときは、必ず force: true を使用します。

// ❌ WRONG: Trying to delete store with documents
await ai.fileSearchStores.delete({
  name: fileStore.name
})
// Error: Cannot delete store with documents

// ✅ CORRECT: Use force delete
await ai.fileSearchStores.delete({
  name: fileStore.name,
  force: true  // Deletes store AND all documents
})

// Alternative: Delete documents first
const docs = await ai.fileSearchStores.documents.list({ parent: fileStore.name })
for (const doc of docs.documents || []) {
  await ai.fileSearchStores.documents.delete({
    name: doc.name,
    force: true
  })
}
await ai.fileSearchStores.delete({ name: fileStore.name })

出典: https://ai.google.dev/api/file-search/file-search-stores#delete

エラー 8:サポートされていないモデルを使用する

症状:

Error: File Search is only supported for Gemini 2.5 Pro and Flash models

原因: File Search には Gemini 2.5 Pro または Gemini 2.5 Flash が必要です。Gemini 1.5 モデルはサポートされていません。

防止策: 必ず 2.5 モデルを使用します。

// ❌ WRONG: Using Gemini 1.5 model
const response = await ai.models.generateContent({
  model: 'gemini-1.5-pro',  // Not supported!
  contents: 'What is the installation procedure?',
  config: {
    tools: [{
      fileSearch: { fileSearchStoreNames: [fileStore.name] }
    }]
  }
})

// ✅ CORRECT: Use Gemini 2.5 models
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',  // ✅ Supported (fast, cost-effective)
  // OR
  // model: 'gemini-2.5-pro',   // ✅ Supported (higher quality)
  contents: 'What is the installation procedure?',
  config: {
    tools: [{
      fileSearch: { fileSearchStoreNames: [fileStore.name] }
    }]
  }
})

出典: https://ai.google.dev/gemini-api/docs/file-search

セットアップ手順

ステップ 1:クライアントを初期化する
import { GoogleGenAI } from '@google/genai'
import fs from 'fs'

// Initialize client with API key
const ai = new GoogleGenAI({
  apiKey: process.env.GOOGLE_API_KEY
})

// Verify API key is set
if (!process.env.GOOGLE_API_KEY) {
  throw new Error('GOOGLE_API_KEY environment variable is required')
}
ステップ 2:File Search ストアを作成する
// Create a store (container for documents)
const fileStore = await ai.fileSearchStores.create({
  config: {
    displayName: 'my-knowledge-base',  // Human-readable name
    // Optional: Add store-level metadata
    customMetadata: {
      project: 'customer-support',
      environment: 'production'
    }
  }
})

console.log('Created store:', fileStore.name)
// Output: fileSearchStores/abc123xyz...

既存のストアを検索する:

// List all stores (paginated)
const stores = await ai.fileSearchStores.list({
  pageSize: 20  // Max 20 per page
})

// Find by display name
let targetStore = null
let pageToken = null

do {
  const page = await ai.fileSearchStores.list({ pageToken })
  targetStore = page.fileSearchStores.find(
    s => s.displayName === 'my-knowledge-base'
  )
  pageToken = page.nextPageToken
} while (!targetStore && pageToken)

if (targetStore) {
  console.log('Found existing store:', targetStore.name)
} else {
  console.log('Store not found, creating new one...')
}
ステップ 3:ドキュメントをアップロードする

単一ファイルのアップロード:

const operation = await ai.fileSearchStores.uploadToFileSearchStore({
  name: fileStore.name,
  file: fs.createReadStream('./docs/manual.pdf'),
  config: {
    displayName: 'Installation Manual',
    customMetadata: {
      doc_type: 'manual',
      version: '1.0',
      language: 'en'
    },
    chunkingConfig: {
      whiteSpaceConfig: {
        maxTokensPerChunk: 500,
        maxOverlapTokens: 50
      }
    }
  }
})

// Poll until indexing complete
while (!operation.done) {
  await new Promise(resolve => setTimeout(resolve, 1000))
  operation = await ai.operations.get({ name: operation.name })
}

console.log('✅ Indexed:', operation.response.displayName)

一括アップロード(並行処理):

const filePaths = [
  './docs/manual.pdf',
  './docs/faq.md',
  './docs/troubleshooting.docx'
]

// Upload all files concurrently
const uploadPromises = filePaths.map(filePath =>
  ai.fileSearchStores.uploadToFileSearchStore({
    name: fileStore.name,
    file: fs.createReadStream(filePath),
    config: {
      displayName: filePath.split('/').pop(),
      customMetadata: {
        doc_type: 'support',
        source_path: filePath
      },
      chunkingConfig: {
        whiteSpaceConfig: {
          maxTokensPerChunk: 500,
          maxOverlapTokens: 50
        }
      }
    }
  })
)

const operations = await Promise.all(uploadPromises)

// Poll all operations
for (const operation of operations) {
  let op = operation
  while (!op.done) {
    await new Promise(resolve => setTimeout(resolve, 1000))
    op = await ai.operations.get({ name: op.name })
  }
  console.log('✅ Indexed:', op.response.displayName)
}
ステップ 4:File Search でクエリを実行する

基本的なクエリ:

const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'What are the safety precautions for installation?',
  config: {
    tools: [{
      fileSearch: {
        fileSearchStoreNames: [fileStore.name]
      }
    }]
  }
})

console.log('Answer:', response.text)

// Access citations
const grounding = response.candidates[0].groundingMetadata
if (grounding?.groundingChunks) {
  console.log('\nSources:')
  grounding.groundingChunks.forEach((chunk, i) => {
    console.log(`${i + 1}. ${chunk.retrievedContext?.title || 'Unknown'}`)
    console.log(`   URI: ${chunk.retrievedContext?.uri || 'N/A'}`)
  })
}

メタデータフィルタリングを使用したクエリ:

const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'How do I reset the device?',
  config: {
    tools: [{
      fileSearch: {
        fileSearchStoreNames: [fileStore.name],
        // Filter to only search troubleshooting docs in English, version 1.0
        metadataFilter: 'doc_type="troubleshooting" AND language="en" AND version="1.0"'
      }
    }]
  }
})

console.log('Answer:', response.text)

メタデータフィルターの構文:

  • AND:key1="value1" AND key2="value2"
  • OR:key1="value1" OR key1="value2"
  • 括弧:(key1="a" OR key1="b") AND key2="c"
ステップ 5:ドキュメントの一覧表示と管理
// List all documents in store
const docs = await ai.fileSearchStores.documents.list({
  parent: fileStore.name,
  pageSize: 20
})

console.log(`Total documents: ${docs.documents?.length || 0}`)

docs.documents?.forEach(doc => {
  console.log(`- ${doc.displayName} (${doc.name})`)
  console.log(`  Metadata:`, doc.customMetadata)
})

// Get specific document details
const docDetails = await ai.fileSearchStores.documents.get({
  name: docs.documents[0].name
})

console.log('Document details:', docDetails)

// Delete document
await ai.fileSearchStores.documents.delete({
  name: docs.documents[0].name,
  force: true
})
ステップ 6:クリーンアップ
// Delete entire store (force deletes all documents)
await ai.fileSearchStores.delete({
  name: fileStore.name,
  force: true
})

console.log('✅ Store deleted')

推奨するチャンク分割戦略

チャンク分割の設定は、検索品質に大きく影響します。コンテンツの種類に応じて調整してください。

技術ドキュメント
chunkingConfig: {
  whiteSpaceConfig: {
    maxTokensPerChunk: 500,   // Smaller chunks for precise code/API lookup
    maxOverlapTokens: 50      // 10% overlap
  }
}

最適な用途: API ドキュメント、SDK リファレンス、コード例、設定ガイド

散文と記事
chunkingConfig: {
  whiteSpaceConfig: {
    maxTokensPerChunk: 800,   // Larger chunks preserve narrative flow
    maxOverlapTokens: 80      // 10% overlap
  }
}

最適な用途: ブログ投稿、ニュース記事、製品説明、マーケティング資料

法律文書と契約書
chunkingConfig: {
  whiteSpaceConfig: {
    maxTokensPerChunk: 300,   // Very small chunks for high precision
    maxOverlapTokens: 30      // 10% overlap
  }
}

最適な用途: 法律文書、契約書、規制、コンプライアンス文書

FAQ とサポート
chunkingConfig: {
  whiteSpaceConfig: {
    maxTokensPerChunk: 400,   // Medium chunks (1-2 Q&A pairs)
    maxOverlapTokens: 40      // 10% overlap
  }
}

最適な用途: FAQ、トラブルシューティングガイド、ハウツー記事

一般的なルール: チャンクの境界でコンテキストが失われないよう、10%のオーバーラップ(オーバーラップ = チャンクサイズ / 10)を維持してください。

メタデータのベストプラクティス

フィルタリングと整理のために、メタデータスキーマを設計します。

例:カスタマーサポートのナレッジベース
customMetadata: {
  doc_type: 'faq' | 'manual' | 'troubleshooting' | 'guide',
  product: 'widget-pro' | 'widget-lite',
  version: '1.0' | '2.0',
  language: 'en' | 'es' | 'fr',
  category: 'installation' | 'configuration' | 'maintenance',
  priority: 'critical' | 'normal' | 'low',
  last_updated: '2025-01-15',
  author: 'support-team'
}

クエリの例:

metadataFilter: 'product="widget-pro" AND (doc_type="troubleshooting" OR doc_type="faq") AND language="en"'
例:法律文書リポジトリ
customMetadata: {
  doc_type: 'contract' | 'regulation' | 'case-law' | 'policy',
  jurisdiction: 'US' | 'EU' | 'UK',
  practice_area: 'employment' | 'corporate' | 'ip' | 'tax',
  effective_date: '2025-01-01',
  status: 'active' | 'archived',
  confidentiality: 'public' | 'internal' | 'privileged'
}
例:コードドキュメント
customMetadata: {
  doc_type: 'api-reference' | 'tutorial' | 'example' | 'changelog',
  language: 'javascript' | 'python' | 'java' | 'go',
  framework: 'react' | 'nextjs' | 'express' | 'fastapi',
  version: '1.2.0',
  difficulty: 'beginner' | 'intermediate' | 'advanced'
}

ヒント:

  • 一貫したキーの命名規則(snake_case または camelCase)を使用する
  • フィルタリング可能な重要フィールドに絞る(最大 20 個)
  • 値には列挙型や定数を使用する(フィルタリングが容易になります)
  • 時間ベースのフィルタリング用にバージョンと日付のフィールドを含める

コストの最適化

1. アップロード前に重複を排除する
// Track uploaded file hashes to avoid duplicates
const uploadedHashes = new Set<string>()

async function uploadWithDeduplication(filePath: string) {
  const fileHash = await getFileHash(filePath)

  if (uploadedHashes.has(fileHash)) {
    console.log(`Skipping duplicate: ${filePath}`)
    return
  }

  await ai.fileSearchStores.uploadToFileSearchStore({
    name: fileStore.name,
    file: fs.createReadStream(filePath)
  })

  uploadedHashes.add(fileHash)
}
2. 大容量ファイルを圧縮する
// Convert images to text before indexing (OCR)
// Compress PDFs (remove images, use text-only)
// Use markdown instead of Word docs (smaller size)
3. メタデータフィルタリングでクエリの対象範囲を絞る
// ❌ EXPENSIVE: Search all 10GB of documents
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'Reset procedure?',
  config: {
    tools: [{ fileSearch: { fileSearchStoreNames: [fileStore.name] } }]
  }
})

// ✅ CHEAPER: Filter to only troubleshooting docs (subset)
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'Reset procedure?',
  config: {
    tools: [{
      fileSearch: {
        fileSearchStoreNames: [fileStore.name],
        metadataFilter: 'doc_type="troubleshooting"'  // Reduces search scope
      }
    }]
  }
})
4. コストを削減するため、Pro ではなく Flash を選択する
// Gemini 2.5 Flash is 10x cheaper than Pro for queries
// Use Flash unless you need Pro's advanced reasoning

// Development/testing: Use Flash
model: 'gemini-2.5-flash'

// Production (high-stakes answers): Use Pro
model: 'gemini-2.5-pro'
5. ストレージの使用量を監視する
// List stores and estimate storage
const stores = await ai.fileSearchStores.list()

for (const store of stores.fileSearchStores || []) {
  const docs = await ai.fileSearchStores.documents.list({
    parent: store.name
  })

  console.log(`Store: ${store.displayName}`)
  console.log(`Documents: ${docs.documents?.length || 0}`)
  // Estimate storage (3x input size)
  console.log(`Estimated storage: ~${(docs.documents?.length || 0) * 10} MB`)
}

テストと検証

ストアの作成を確認する
const store = await ai.fileSearchStores.get({
  name: fileStore.name
})

console.assert(store.displayName === 'my-knowledge-base', 'Store name mismatch')
console.log('✅ Store created successfully')
ドキュメントのインデックス作成を確認する
const docs = await ai.fileSearchStores.documents.list({
  parent: fileStore.name
})

console.assert(docs.documents?.length > 0, 'No documents indexed')
console.log(`✅ ${docs.documents?.length} documents indexed`)
クエリ機能を確認する
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'What is this knowledge base about?',
  config: {
    tools: [{ fileSearch: { fileSearchStoreNames: [fileStore.name] } }]
  }
})

console.assert(response.text.length > 0, 'Empty response')
console.log('✅ Query successful:', response.text.substring(0, 100) + '...')
引用を確認する
const response = await ai.models.generateContent({
  model: 'gemini-2.5-flash',
  contents: 'Provide a specific answer with citations.',
  config: {
    tools: [{ fileSearch: { fileSearchStoreNames: [fileStore.name] } }]
  }
})

const grounding = response.candidates[0].groundingMetadata
console.assert(
  grounding?.groundingChunks?.length > 0,
  'No grounding/citations returned'
)
console.log(`✅ ${grounding?.groundingChunks?.length} citations returned`)

統合例

このスキルには、templates/ ディレクトリに 3 つの実用的なテンプレートが含まれています。

テンプレート 1:basic-node-rag

以下を実演する、最小構成の Node.js/TypeScript の例です。

  • ファイル検索ストアを作成する
  • 複数のドキュメントをアップロードする
  • 自然言語でクエリを実行する
  • 引用を表示する

使用する場面: File Search の学習、プロトタイピング、シンプルな CLI ツール

実行:

cd templates/basic-node-rag
npm install
npm run dev
テンプレート 2:cloudflare-worker-rag

以下を紹介する Cloudflare Workers との統合例です。

  • ドキュメントアップロード用のエッジ API
  • セマンティック検索用のエッジ API
  • ドキュメントストレージに R2 を統合する
  • ハイブリッドアーキテクチャ(Gemini File Search + Cloudflare エッジ)

使用する場面: グローバルなエッジアプリケーションの構築、Cloudflare スタックとの統合

デプロイ:

cd templates/cloudflare-worker-rag
npm install
npx wrangler deploy
テンプレート 3:nextjs-docs-search

以下の機能を備えたフルスタックの Next.js アプリケーションです。

  • ドラッグ&ドロップ対応のドキュメントアップロード UI
  • リアルタイム検索インターフェース
  • ソースリンク付きの引用表示
  • メタデータフィルタリング UI

使用する場面: 本番用ドキュメントサイトやナレッジベースの構築

実行:

cd templates/nextjs-docs-search
npm install
npm run dev

比較:File Search と代替サービス

機能Gemini File SearchCloudflare VectorizeOpenAI Files API
セットアップの複雑さシンプル(単一の API)中程度(DIY RAG)シンプル(単一の API)
ファイル形式のサポート100 種類以上手動(テキスト/埋め込み)20 種類以上
最大ファイルサイズ100 MB該当なし512 MB
ストアあたりの最大ファイル数不明無制限10,000
カスタム埋め込みなし(Gemini のみ)あり(任意のモデル)なし(OpenAI のみ)
チャンク分割の制御限定的(トークンベース)完全に制御可能なし
グローバル分散なし(米国中心)あり(エッジネットワーク)なし
引用あり(自動)手動で実装あり(自動)
メタデータのフィルタリングあり(20 フィールド)あり(無制限)あり
ストリーミング更新なし(削除して再アップロード)あり(AutoRAG)なし
料金モデルインデックス作成ごとに課金使用量ベースストレージベース
無料枠1 GB のストレージDeveloper tier1 GB のストレージ
TypeScript SDK完全にサポート完全にサポート完全にサポート

コスト比較(ナレッジベース 10 GB、1 年間):

Gemini File Search:

  • インデックス作成:10GB ≈ 2.5B トークン × $0.15/1M = 初回のみ $375
  • ストレージ:無料(Tier 1 で 10 GB をカバー)
  • クエリ:標準料金
  • 1 年目の合計: $375

OpenAI Files API:

  • インデックス作成:無料
  • ストレージ:10GB × $0.10/GB/day = 年間 $365
  • クエリ:標準料金
  • 1 年目の合計: $365

Cloudflare Vectorize(DIY):

  • インデックス作成:Workers AI の埋め込み(変動)
  • ストレージ:Vectorize の料金
  • クエリ:Workers AI の料金
  • 1 年目の合計: ~$100-500(使用状況によって異なります)

勝者: 1 年目は OpenAI、2 年目以降は Gemini(更新頻度が低い場合)

トラブルシューティング

問題:「API key not valid」

原因: API キーが無効、または設定されていません

解決策:

# Verify API key is set
echo $GOOGLE_API_KEY

# If missing, set it
export GOOGLE_API_KEY="your-api-key-here"

# Or add to .env file
echo "GOOGLE_API_KEY=your-api-key-here" >> .env
問題:「Quota exceeded」

原因: 無料枠の上限(ストレージ 1 GB または 1 日あたり 1,500 リクエスト)を超過しています

解決策:

  • 古いストアを削除します:ai.fileSearchStores.delete({ name, force: true })
  • 有料プランにアップグレードします:https://ai.google.dev/pricing
  • クォータがリセットされるまで待ちます(毎日 UTC の午前 0 時)
問題:「File format not supported」

原因: アップロードされたファイル形式が、サポート対象の 100 種類以上の形式に含まれていません

解決策:

  • サポートされている MIME タイプを確認します:https://ai.google.dev/api/file-search/documents
  • サポートされている形式に変換します(例:DOCX を PDF に変換する、OCR を使って画像をテキストに変換する)
問題:「No results returned」

原因: ドキュメントのインデックス作成が完了していないか、クエリが曖昧すぎます

解決策:

  • インデックス作成が完了していることを確認します:done: true になるまでオペレーションをポーリングします
  • ドキュメント数を確認します:ai.fileSearchStores.documents.list()
  • クエリを絞り込みます:より具体的にします
  • メタデータフィルターを調整します:条件が厳しすぎる可能性があります
問題:「Poor retrieval quality」

原因: チャンク分割の設定が最適ではありません

解決策:

  • より正確に検索できるよう、maxTokensPerChunk の値を小さくします(300~500 をお試しください)
  • オーバーラップをチャンクサイズの 10~15% に増やします
  • 新しいチャンク分割設定でドキュメントを再アップロードします

参考資料

公式ドキュメント:

チュートリアル:

このスキルに含まれるリソース:

  • references/api-reference.md - API ドキュメントの完全版
  • references/chunking-best-practices.md - チャンク分割の詳細な戦略
  • references/pricing-calculator.md - コスト見積もりガイド
  • references/migration-from-openai.md - OpenAI Files API からの移行ガイド
  • scripts/create-store.ts - ストアを作成する CLI ツール
  • scripts/upload-batch.ts - バッチアップロードスクリプト
  • scripts/query-store.ts - 対話型クエリツール
  • scripts/cleanup.ts - クリーンアップスクリプト

作業用テンプレート:

  • templates/basic-node-rag/ - 最小構成の Node.js サンプル
  • templates/cloudflare-worker-rag/ - エッジデプロイのサンプル
  • templates/nextjs-docs-search/ - フルスタックの Next.js アプリ

スキルのバージョン: 1.0.0 最終確認日: 2025-11-10 パッケージのバージョン: @google/genai ^0.21.0 トークン削減率: 約65% 防止したエラー数: 8

コメント