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 美元;存储免费(有额度限制);查询时嵌入免费

何时使用此技能

理想用例:

  • 构建客户支持知识库(手册、常见问题、故障排除指南)
  • 创建内部文档搜索(公司 Wiki、政策、流程)
  • 法律/合规文档分析(合同、法规、判例法)
  • 研究工具(学术论文、文章、教科书)
  • 代码文档搜索(API 文档、SDK 参考资料、示例)
  • 产品信息检索(规格、数据表、用户指南)

适合使用 File Search 的情况:

  • ✅ 需要完全托管的 RAG 解决方案(无需设置向量数据库)
  • ✅ 重视成本可预测性(按索引付费,而非持续支付存储费用)
  • ✅ 需要广泛支持文件格式(开箱即支持 100 多种格式)
  • ✅ 引用很重要(内置依据元数据)
  • ✅ 优先考虑简单部署(只需设置一个 API)
  • ✅ 文档相对静态(更新不频繁)

以下情况改用 Cloudflare Vectorize/AutoRAG:

  • ✅ 全球边缘性能至关重要(实现全球低延迟)
  • ✅ 在 Cloudflare 上构建全栈应用(Workers、R2、D1)
  • ✅ 需要自定义嵌入模型或检索逻辑
  • ✅ 需要从 R2 或外部来源实时更新数据

以下情况改用 OpenAI Files API:

  • ✅ 已在使用 OpenAI Assistants API(对话线程)
  • ✅ 需要将知识关联到持久化的助手线程
  • ✅ 处理非常庞大的文件集合(每个存储空间 10,000 多个文件)
  • ✅ 偏好基于存储量的定价模式(每天每 GB 0.10 美元)

何时不应使用此技能

以下情况请跳过此技能:

  • ❌ 需要自定义嵌入模型(File Search 仅支持 Gemini Embeddings)
  • ❌ 文档更新频繁(不支持流式更新,必须先删除再重新上传)
  • ❌ 构建对话式 AI Agent(请改用 OpenAI Assistants 或 Claude MCP)
  • ❌ 需要 BM25/混合搜索(File Search 仅支持向量搜索)
  • ❌ 需要高级重排配置(仅自动配置)
  • ❌ 需要从 PDF 中解析图像/表格(仅支持文本提取)

前置条件

1. Google AI API Key

在 https://aistudio.google.com/apikey 创建 API 密钥

免费层级限额:

  • 1 GB 存储空间(所有文件搜索存储库的总量)
  • 每天 1,500 次请求
  • 每分钟 100 万个 token

付费层级定价:

  • 索引:每 100 万个输入 token 收费 $0.15(一次性)
  • 存储:免费(Tier 1:10 GB,Tier 2:100 GB,Tier 3:1 TB)
  • 查询时的嵌入:免费(检索到的上下文计为输入 token)
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
  }
}

避免常见错误

此技能可避免实现文件搜索时遇到的 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
      }
    }
  }
})

分块指南:

  • 技术文档/代码: 每块 500 个 token,重叠 50 个 token
  • 散文/文章: 每块 800 个 token,重叠 80 个 token
  • 法律文件/合同: 每块 300 个 token,重叠 30 个 token(高精度)

来源: 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 的账单。

原因: 索引费用为一次性费用,但按输入 token 计费(每 100 万个 token 收费 $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 亿个 token = 索引费用 $37.50
  • 100 MB PDF ≈ 2,500 万个 token = 索引费用 $3.75
  • 10 MB 代码 ≈ 250 万个 token = 索引费用 $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

原因: 文件搜索需要 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:创建文件搜索存储库
// 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 步:使用文件搜索进行查询

基本查询:

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
  }
}

适用于: 法律文档、合同、法规、合规文档

常见问题和支持文档
chunkingConfig: {
  whiteSpaceConfig: {
    maxTokensPerChunk: 400,   // Medium chunks (1-2 Q&A pairs)
    maxOverlapTokens: 40      // 10% overlap
  }
}

适用于: 常见问题、故障排查指南、操作指南文章

一般规则: 保持 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. 选择 Flash 而非 Pro,以节省成本
// 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 示例,演示如何:

  • 创建文件搜索存储
  • 上传多个文档
  • 使用自然语言进行查询
  • 显示引用

适用场景: 学习文件搜索、快速构建原型、制作简单的 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 应用,包含:

  • 支持拖放的文档上传界面
  • 实时搜索界面
  • 带来源链接的引用呈现
  • 元数据过滤界面

适用场景: 构建生产级文档网站、知识库

运行:

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

对比:File Search 与替代方案

功能Gemini File SearchCloudflare VectorizeOpenAI Files API
设置复杂度简单(单个 API)中等(自行搭建 RAG)简单(单个 API)
文件格式支持100 多种手动处理(文本/嵌入)20 多种
最大文件大小100 MB不适用512 MB
每个存储库的最大文件数未知无限制10,000
自定义嵌入不支持(仅 Gemini)支持(任意模型)不支持(仅 OpenAI)
分块控制有限(基于 token)完全控制无
全球分发不支持(以美国为中心)支持(边缘网络)不支持
引用支持(自动)需手动实现支持(自动)
元数据筛选支持(20 个字段)支持(无限制)支持
流式更新不支持(删除后重新上传)支持(AutoRAG)不支持
定价模式按索引付费按使用量计费按存储量计费
免费套餐1 GB 存储开发者套餐1 GB 存储
TypeScript SDK完全支持完全支持完全支持

成本对比(10 GB 知识库,1 年):

Gemini File Search:

  • 索引:10GB ≈ 2.5B 个 token × $0.15/1M = 一次性 $375
  • 存储:免费(第 1 层涵盖 10 GB)
  • 查询:标准定价
  • 第 1 年总计: $375

OpenAI Files API:

  • 索引:免费
  • 存储:10GB × $0.10/GB/day = 每年 $365
  • 查询:标准定价
  • 第 1 年总计: $365

Cloudflare Vectorize(自行搭建):

  • 索引: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,500 次请求)

解决方案:

  • 删除旧存储库:ai.fileSearchStores.delete({ name, force: true })
  • 升级至付费套餐:https://ai.google.dev/pricing
  • 等待配额重置(每天 UTC 午夜)
问题:“File format not supported”

原因: 上传的文件类型不在 100 多种受支持的格式中

解决方案:

问题:“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 Token 节省: ~65% 避免的错误: 8

评论

支持 Markdown