google-gemini-file-search について
Google Gemini File Search によるマネージド RAG の設定と連携を解説する開発者向けガイドです。文書のアップロード、インデックス作成、チャンク設定、検索、実装上の問題への対処を扱います。
主な機能
- 01File Search ストアの作成と管理
- 02文書のアップロードとインデックス作成
- 03チャンクとメタデータの設定
- 04自然言語による文書検索
- 05検索結果の引用処理
- 06一般的な実装エラーの予防
活用シーン
- 01文書に基づく質疑応答の構築
- 02アプリへのマネージド RAG の追加
- 03メタデータによる文書検索の絞り込み
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill google-gemini-file-searchインストール後、Claude Code で/google-gemini-file-searchを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEPROJECT_STATUS.mdREADME.md.claude-plugin/plugin.jsonreferences/README.mdscripts/README.mdcreate-store.tstemplates/README.md詳細
Google Gemini File Search のセットアップ
概要
Google Gemini File Search は、完全マネージド型の RAG システムです。ドキュメント(PDF、Word、Excel、コードなど、100 以上の形式)をアップロードし、自然言語で検索できます。チャンク分割、埋め込み、セマンティック検索、引用は自動で行われます。
このスキルでできること:
- @google/genai File Search API の完全なセットアップ
- 8 つのエラーとその予防策を解説
- 最適な検索のためのチャンク分割のベストプラクティス
- コストの最適化(インデックス作成は 100 万トークンあたり $0.15、ストレージは 3 倍の容量が必要)
- Cloudflare Workers + Next.js の統合テンプレート
前提条件
1. Google AI API キー
https://aistudio.google.com/apikey で API キーを作成します。
無料枠の制限:
- 1 GB のストレージ(すべての File Search ストアの合計)
- 1 日あたり 1,500 リクエスト
- 1 分あたり 100 万トークン
有料枠の料金:
- インデックス作成:入力トークン 100 万件あたり $0.15(初回のみ)
- ストレージ:無料(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
現在の安定版: 1.30.0 以降(npm view @google/genai version で確認してください)
⚠️ 重要: File Search API を使用するには、@google/genai v1.29.0 以降が必要です。それ以前のバージョンは File Search をサポートしていません。この API は v1.29.0 で追加されました(2025 年 11 月 5 日)。
4. TypeScript の設定(任意ですが推奨)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
}
}
よくあるエラーの防止
このスキルでは、File Search の実装時によく発生する 12 個のエラーを防ぎます。
エラー 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 ドルの予想外の請求が発生する。
原因: インデックス作成の料金は初回のみですが、入力トークン数に応じて計算されます(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 with timeout
const operation = await ai.fileSearchStores.uploadToFileSearchStore({
name: fileStore.name,
file: fs.createReadStream('large.pdf')
})
// Poll with timeout and fallback
const MAX_POLL_TIME = 60000 // 60 seconds
const POLL_INTERVAL = 1000
let elapsed = 0
while (!operation.done && elapsed < MAX_POLL_TIME) {
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL))
elapsed += POLL_INTERVAL
try {
operation = await ai.operations.get({ name: operation.name })
console.log(`Indexing progress: ${operation.metadata?.progress || 'processing...'}`)
} catch (error) {
console.warn('Polling failed, assuming complete:', error)
break
}
}
if (operation.error) {
throw new Error(`Indexing failed: ${operation.error.message}`)
}
// ⚠️ Warning: operations.get() can be unreliable for large files
// If timeout reached, verify document exists manually
if (elapsed >= MAX_POLL_TIME) {
console.warn('Polling timeout - verifying document manually')
const docs = await ai.fileSearchStores.documents.list({ parent: fileStore.name })
const uploaded = docs.documents?.find(d => d.displayName === 'large.pdf')
if (uploaded) {
console.log('✅ Document found despite polling timeout')
} else {
throw new Error('Upload failed - document not found')
}
}
console.log('✅ Indexing complete:', operation.response?.displayName)
出典: https://ai.google.dev/api/file-search/file-search-stores#uploadtofilesearchstore, GitHub Issue #1211
エラー 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 3 Pro and Flash models
原因: File Search には Gemini 3 Pro または Gemini 3 Flash が必要です。Gemini 2.x と 1.5 のモデルはサポートされていません。
防止策: 必ず Gemini 3 モデルを使用してください。
// ❌ 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 3 models
const response = await ai.models.generateContent({
model: 'gemini-3-flash', // ✅ Supported (fast, cost-effective)
// OR
// model: 'gemini-3-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
エラー 9:Blob ソースで displayName が保持されない(v1.34.0 以降で修正済み)
症状:
groundingChunks[0].title === null // No document source shown
原因: @google/genai の v1.34.0 より前のバージョンでは、ファイルパスではなく Blob オブジェクトとしてファイルをアップロードすると、SDK が displayName と customMetadata の設定フィールドを破棄していました。
防止策:
// ✅ CORRECT: Upgrade to v1.34.0+ for automatic fix
npm install @google/genai@latest // v1.34.0+
await ai.fileSearchStores.uploadToFileSearchStore({
name: storeName,
file: new Blob([arrayBuffer], { type: 'application/pdf' }),
config: {
displayName: 'Safety Manual.pdf', // ✅ Now preserved
customMetadata: { version: '1.0' } // ✅ Now preserved
}
})
// ⚠️ WORKAROUND for v1.33.0 and earlier: Use resumable upload
const uploadUrl = `https://generativelanguage.googleapis.com/upload/v1beta/${storeName}:uploadToFileSearchStore?key=${API_KEY}`
// Step 1: Initiate with displayName in body
const initResponse = await fetch(uploadUrl, {
method: 'POST',
headers: {
'X-Goog-Upload-Protocol': 'resumable',
'X-Goog-Upload-Command': 'start',
'X-Goog-Upload-Header-Content-Length': numBytes.toString(),
'X-Goog-Upload-Header-Content-Type': 'application/pdf',
'Content-Type': 'application/json'
},
body: JSON.stringify({
displayName: 'Safety Manual.pdf' // ✅ Works with resumable upload
})
})
// Step 2: Upload file bytes
const uploadUrl2 = initResponse.headers.get('X-Goog-Upload-URL')
await fetch(uploadUrl2, {
method: 'PUT',
headers: {
'Content-Length': numBytes.toString(),
'X-Goog-Upload-Offset': '0',
'X-Goog-Upload-Command': 'upload, finalize',
'Content-Type': 'application/pdf'
},
body: fileBytes
})
参照元: GitHub Issue #1078
エラー 10:JSON レスポンスモードでグラウンディングメタデータが無視される
症状:
response.candidates[0].groundingMetadata === undefined
// Even though fileSearch tool is configured
原因: 構造化出力に responseMimeType: 'application/json' を使用すると、Gemini 3 モデルであっても、API は fileSearch ツールを無視し、グラウンディングメタデータを返しません。
防止策:
// ❌ WRONG: Structured output overrides grounding
const response = await ai.models.generateContent({
model: 'gemini-3-flash',
contents: 'Summarize guidelines',
config: {
responseMimeType: 'application/json', // Loses grounding
tools: [{ fileSearch: { fileSearchStoreNames: [storeName] } }]
}
})
// ✅ CORRECT: Two-step approach
// Step 1: Get grounded text response
const textResponse = await ai.models.generateContent({
model: 'gemini-3-flash',
contents: 'Summarize guidelines',
config: {
tools: [{ fileSearch: { fileSearchStoreNames: [storeName] } }]
}
})
const grounding = textResponse.candidates[0].groundingMetadata
// Step 2: Convert to structured format in prompt
const jsonResponse = await ai.models.generateContent({
model: 'gemini-3-flash',
contents: `Convert to JSON: ${textResponse.text}
Format:
{
"summary": "...",
"key_points": ["..."]
}`,
config: {
responseMimeType: 'application/json',
responseSchema: {
type: 'object',
properties: {
summary: { type: 'string' },
key_points: { type: 'array', items: { type: 'string' } }
}
}
}
})
// Combine results
const result = {
data: JSON.parse(jsonResponse.text),
sources: grounding.groundingChunks
}
参照元: GitHub Issue #829
エラー 11:Google Search と File Search のツールは同時に使用できない
症状:
Error: "Search as a tool and file search tool are not supported together"
Status: INVALID_ARGUMENT
原因: Gemini API では、同じリクエスト内で googleSearch と fileSearch のツールを使用できません。
防止策:
// ❌ WRONG: Combining search tools
const response = await ai.models.generateContent({
model: 'gemini-3-flash',
contents: 'What are the latest industry guidelines?',
config: {
tools: [
{ googleSearch: {} },
{ fileSearch: { fileSearchStoreNames: [storeName] } }
]
}
})
// ✅ CORRECT: Use separate specialist agents
async function searchWeb(query: string) {
return ai.models.generateContent({
model: 'gemini-3-flash',
contents: query,
config: { tools: [{ googleSearch: {} }] }
})
}
async function searchDocuments(query: string) {
return ai.models.generateContent({
model: 'gemini-3-flash',
contents: query,
config: { tools: [{ fileSearch: { fileSearchStoreNames: [storeName] } }] }
})
}
// Orchestrate based on query type
const needsWeb = query.includes('latest') || query.includes('current')
const response = needsWeb
? await searchWeb(query)
: await searchDocuments(query)
参照元: GitHub Issue #435、Google Codelabs
エラー 12:Batch API でレスポンスメタデータが欠落する(コミュニティ提供)
症状: metadata フィールドを使用すると、バッチレスポンスをリクエストと関連付けられません。
原因: metadata フィールドを含む InlinedRequest で Batch API を使用すると、対応する InlinedResponse に metadata が返されません。
防止策:
// ❌ WRONG: Expecting metadata in response
const batchRequest = {
metadata: { key: 'my-request-id' },
contents: [{ parts: [{ text: 'Question?' }], role: 'user' }],
config: {
tools: [{ fileSearch: { fileSearchStoreNames: [storeName] } }]
}
}
const batchResponse = await ai.batch.create({ requests: [batchRequest] })
console.log(batchResponse.responses[0].metadata) // ❌ undefined
// ✅ CORRECT: Use array index to correlate
const requests = [
{ metadata: { id: 'req-1' }, contents: [...] },
{ metadata: { id: 'req-2' }, contents: [...] }
]
const responses = await ai.batch.create({ requests })
// Map by index (not ideal but works)
responses.responses.forEach((response, i) => {
const requestMetadata = requests[i].metadata
console.log(`Response for ${requestMetadata.id}:`, response)
})
コミュニティによる検証: メンテナーが確認し、内部バグとして報告済みです。
参照元: GitHub Issue #1191
セットアップ手順
ステップ 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-3-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-3-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-3-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-3-flash',
contents: 'Reset procedure?',
config: {
tools: [{
fileSearch: {
fileSearchStoreNames: [fileStore.name],
metadataFilter: 'doc_type="troubleshooting"' // Reduces search scope
}
}]
}
})
4. コスト削減のためにProではなくFlashを選ぶ
// Gemini 3 Flash is 10x cheaper than Pro for queries
// Use Flash unless you need Pro's advanced reasoning
// Development/testing: Use Flash
model: 'gemini-3-flash'
// Production (high-stakes answers): Use Pro
model: 'gemini-3-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-3-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-3-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`)
統合例
ストリーミングのサポート
File SearchはgenerateContentStream()を使用したストリーミング応答をサポートしています。
// ✅ Streaming works with File Search (v1.34.0+)
const stream = await ai.models.generateContentStream({
model: 'gemini-3-flash',
contents: 'Summarize the document',
config: {
tools: [{ fileSearch: { fileSearchStoreNames: [storeName] } }]
}
})
for await (const chunk of stream) {
process.stdout.write(chunk.text)
}
// Access grounding after stream completes
const grounding = stream.candidates[0].groundingMetadata
注: SDKの初期バージョン(v1.34.0より前)では、ストリーミングに問題があった可能性があります。安定したストリーミングサポートを利用するには、v1.34.0以降を使用してください。
ソース: GitHub Issue #1221
動作するテンプレート
このスキルには、templates/ディレクトリに動作するテンプレートが3つ含まれています。
テンプレート1:basic-node-rag
以下を実演する、最小構成のNode.js/TypeScriptの例です。
- File Searchストアを作成する
- 複数のドキュメントをアップロードする
- 自然言語でクエリを実行する
- 引用を表示する
用途: 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の概要:https://ai.google.dev/gemini-api/docs/file-search
- APIリファレンス(ストア):https://ai.google.dev/api/file-search/file-search-stores
- APIリファレンス(ドキュメント):https://ai.google.dev/api/file-search/documents
- ブログでのお知らせ:https://blog.google/technology/developers/file-search-gemini-api/
- 料金:https://ai.google.dev/pricing
チュートリアル:
- JavaScript/TypeScriptガイド:https://www.philschmid.de/gemini-file-search-javascript
- SDKリポジトリ:https://github.com/googleapis/js-genai
このスキルに含まれるリソース:
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.1.0 最終検証日: 2026-01-21 パッケージのバージョン: @google/genai ^1.38.0(1.29.0以降が必要) トークン削減率: 約67% 防止したエラー: 12件 変更点: コミュニティ調査で見つかった4つの新しいエラー(displayNameのBlobに関する問題、JSONモードでのgrounding、ツールの競合、一括APIのメタデータ)を追加し、フォールバック検証を含むポーリングのタイムアウト処理パターンを強化し、ストリーミングサポートに関する注記を追加しました。