课程
在本教程中,我将带您用 Google 文件搜索构建一个医疗文档助手。您将了解如何完成搭建、实现查询,并使用自定义分块和元数据过滤等高级功能。结束时,您将明白在何种情况下应选择托管式 RAG,而不是自建技术栈。
什么是 Google 文件搜索工具?
构建 RAG 应用通常意味着要处理向量数据库、嵌入管道以及大量基础设施。Google 于 2025 年 11 月发布的 File Search 工具,通过直接内置在 RAG 系统到 Gemini API 中,消除了这些复杂性。
该工具为您处理复杂环节:对文档进行分块、生成嵌入,并在无需 Pinecone 或 ChromaDB 等外部工具的情况下管理语义搜索。流程相当直接——上传文件、创建存储区,然后开始查询。您还将获得内置引用,可用于验证答案来源。
理解 RAG 以及 Google 如何简化它
Gemini File Search 将自身定位为托管式 RAG 系统。理解 RAG 有助于您更好地使用该工具,并判断其是否适合您的用例。
从本质上说,检索增强生成(RAG)将语言模型与外部知识相连接。在生成响应之前,模型会从您的文档中检索相关信息,使答案基于您的真实数据,而不是仅依赖训练数据。
自建 RAG 的挑战
虽然 RAG 的概念看起来很直接,但自行构建 RAG 管道意味着要管理多个组件:
- 向量数据库:搭建并维护诸如 Pinecone、ChromaDB 或 Weaviate 等服务以存储嵌入
- 嵌入管道:将文档转换为数值向量,并在内容变更时处理更新
- 分块策略:将文档拆分为既能保留上下文、又能保证检索精度的片段
- 基础设施:监控性能、调参,并在数据增长时处理扩展
每个组件都需要专业知识与持续维护。无论是构建需要高可靠性的生产系统,还是追求快速迭代的原型,基础设施开销始终是同样的瓶颈。
为何托管式 RAG 很重要
像 Google 文件搜索这样的托管服务可以消除这一瓶颈。您不再需要调优检索系统,而是只需编写查询;不再调试嵌入管道,而是只需校验结果。基础设施在后台运行,您可以专注于应用逻辑。
Gemini File Search 负责技术复杂性,而您掌控关键事项:索引哪些文档、如何查询以及如何使用结果。当您需要生产级质量、又不想承担运维负担时,这种平衡非常适用。若想更深入了解 RAG 基础,我建议查阅 DataCamp 的 agentic RAG 教程。

理解 Google 文件搜索的最佳方式就是亲手使用。下一节,您将构建一个完整的医疗文档助手,演示从文档上传到基于来源的带引用回答的完整流程。
使用 Google 文件搜索构建医疗文档助手
免责声明:本教程仅出于教学目的,使用 FDA 药品说明书演示 File Search 的功能。您将构建的助手并非用于临床用途、患者护理决策或医疗诊断。医疗建议务必咨询合格的医疗专业人士。即便基于来源文档,AI 系统仍可能生成不正确的信息。
本节将带您使用 File Search 构建一个完整的医疗文档助手。您将基于三种常见药物的 FDA 药品说明书,创建一个可回答药物相互作用、不良反应和禁忌症问题的系统。该助手通过引用源文档的具体段落,提供可验证的答案。
File Search 分两个阶段运行:先对文档进行一次性索引,然后反复进行查询。您将先搭建索引基础设施,随后专注于提问与解读基于来源的回答。
步骤 1:安装 API 并配置认证
需要 Python 3.9 或更高版本。安装 Google Generative AI SDK 及其依赖:
pip install google-genai python-dotenv
从 Google AI Studio获取您的 API 密钥。将其存储在项目目录下的 .env 文件中:
GOOGLE_API_KEY=your_api_key_here
设置导入并初始化客户端:
from google import genai
from google.genai import types
import time
from dotenv import load_dotenv
load_dotenv()
client = genai.Client()
genai.Client() 将使用您的环境变量自动处理认证。后续所有 File Search 操作都将使用该客户端对象。
步骤 2:创建文件搜索存储区
创建一个存储区以保存已索引的文档:
file_search_store = client.file_search_stores.create(
config={"display_name": "fda-drug-labels"}
)
print(f"Created store: {file_search_store.name}")
File Search 存储区相当于您已索引文档的容器。与 48 小时后过期的临时文件上传不同,存储区会永久保留。这意味着您只需索引一次,即可进行成千上万次查询,无需重复上传或处理。
file_search_store.name 包含一个在查询时使用的唯一标识符。其形式类似 fileSearchStores/fdadruglabels-abc123。如果您需要在不同会话中查询该存储区,请保存此值。
步骤 3:上传并索引 PDF 文档
本教程将使用三份 FDA 批准的药品说明书。从 FDA 官网下载以下 PDF:
- Metformin(格华止) - 糖尿病用药
- Atorvastatin(立普妥) - 降胆固醇用药
- Lisinopril(倍他乐克) - 降压用药
将它们保存到项目目录,然后上传到您的 File Search 存储区:
pdf_files = ["metformin.pdf", "atorvastatin.pdf", "lisinopril.pdf"]
for pdf_file in pdf_files:
operation = client.file_search_stores.upload_to_file_search_store(
file=pdf_file,
file_search_store_name=file_search_store.name,
config={"display_name": pdf_file.replace(".pdf", "")},
)
# Wait for indexing to complete
while not operation.done:
time.sleep(3)
operation = client.operations.get(operation)
print(f"{pdf_file} indexed")
在上传期间,File Search 会对每个 PDF 进行分块,并使用 gemini-embedding-001 模型将这些片段转换为嵌入。这些嵌入 是捕捉语义意义的数值表示,使系统即便在您的问题与文档中的表述不完全一致时,也能找到相关段落。
轮询模式(while not operation.done)用于处理异步索引。较大的文档需要更长时间处理,因此 API 会立即返回,您需定期检查完成状态。在生产系统中,考虑加入超时逻辑以避免无限循环。
每个分块都会保留将其关联回源文档及其位置的元数据。在后续访问引用时,此元数据至关重要。
步骤 4:查询单文档信息
现在查询已索引的文档:
query1 = "What are the contraindications for metformin?"
response1 = client.models.generate_content(
model="gemini-2.5-flash",
contents=query1,
config=types.GenerateContentConfig(
tools=[
types.Tool(
file_search=types.FileSearch(
file_search_store_names=[file_search_store.name]
)
)
]
),
)
print(response1.text)
这会打印生成的答案:
Metformin is contraindicated in several conditions:
* Severe renal impairment (eGFR below 30 mL/min/1.73 m2)
* Acute or chronic metabolic acidosis
* Hypersensitivity to metformin
File Search 会检索文档中与您的问题语义最相近的分块,并将其作为上下文提供给 gemini-2.5-flash 以生成答案。tools 数组配置告诉模型在生成过程中使用 File Search。您可以在同一次请求中将 File Search 与其他工具(如代码执行或 Google 搜索)组合使用。
步骤 5:访问引用和基于来源的元数据
提取哪些文档为答案提供了依据:
print("Sources used:")
for i, chunk in enumerate(response1.candidates[0].grounding_metadata.grounding_chunks, 1):
source_name = chunk.retrieved_context.title
print(f" [{i}] {source_name}")
输出:
Sources used:
[1] metformin
[2] atorvastatin
基于来源元数据中的每个分块都包含源文档标题以及影响答案的具体文本段落。这构建了从生成响应回到原始文档的可验证路径——对于准确性至关重要的医疗、法律或金融类应用而言必不可少。
grounding_chunks 数组包含所有被检索到的段落,按相关性排序。尽管查询专门询问的是二甲双胍(metformin),File Search 仍检索了阿托伐他汀(atorvastatin)文档中的内容,可能因为其中也包含相关的禁忌信息。这体现了语义检索的方法:系统寻找概念上相关的内容,而不仅是关键词匹配。
步骤 6:跨多文档查询
测试一个关于药物相互作用的跨文档问题:
query2 = "Can a patient take both atorvastatin and metformin together? Are there any drug interactions?"
response2 = client.models.generate_content(
model="gemini-2.5-flash",
contents=query2,
config=types.GenerateContentConfig(
tools=[
types.Tool(
file_search=types.FileSearch(
file_search_store_names=[file_search_store.name]
)
)
]
),
)
print(response2.text)
相同的 API 模式现在会从多个文档中提取并综合信息。访问被检索的文本片段:
print("Sources used:")
for i, chunk in enumerate(response2.candidates[0].grounding_metadata.grounding_chunks, 1):
source_name = chunk.retrieved_context.title
source_text = chunk.retrieved_context.text[:100] + "..."
print(f" [{i}] {source_name}")
print(f" {source_text}")
输出显示了来自两份药品说明书的摘录:
Sources used:
[1] atorvastatin
Concomitant use with diabetes medications is generally safe but monitor glucose levels...
[2] metformin
Carbonic anhydrase inhibitors may increase the risk of lactic acidosis...
File Search 会从两份文档中检索相关部分,模型则将其综合为连贯的答案。retrieved_context.text 属性为您提供所用的精确段落,便于验证模型没有幻觉信息。
步骤 7:进行跨文档对比
提出一个需要对三份文档进行比较的分析性问题:
query3 = "Which medications have muscle-related side effects?"
response3 = client.models.generate_content(
model="gemini-2.5-flash",
contents=query3,
config=types.GenerateContentConfig(
tools=[
types.Tool(
file_search=types.FileSearch(
file_search_store_names=[file_search_store.name]
)
)
]
),
)
print(response3.text)
# Check which documents were consulted
metadata = response3.candidates[0].grounding_metadata
for i, chunk in enumerate(metadata.grounding_chunks, 1):
print(f" [{i}] {chunk.retrieved_context.title}")
输出会识别出阿托伐他汀存在肌肉相关不良反应(肌痛、肌病、横纹肌溶解),并确认其他药物未列出此类不良反应。基于来源的元数据显示,File Search 在回答该对比问题时参考了三份文档。
至此,您已构建了一个可用的医疗文档助手。核心流程保持一致:在 generate_content() 调用中配置 File Search 工具,获取响应文本,并访问基于来源的元数据进行验证。存储区保留在 Google 的服务器上,因此您可以在未来会话中无需重新索引就继续查询。
接下来,您将探索自定义分块配置和元数据过滤等高级功能,以更精细地控制检索行为。
Google 文件搜索工具的高级功能与定制
基础的 File Search 流程覆盖了大多数用例,但生产系统往往需要对检索行为进行更细致的控制。本节将展示如何自定义分块策略、通过元数据过滤文档、优化性能,并为不同用例管理多个存储区。
自定义分块配置
File Search 会在索引阶段自动将文档拆分为分块。默认策略针对通用文档进行了优化,但当特定文档类型需要不同处理时,您可以自定义该行为。
以医疗助手为例。药品说明书包含密集的技术信息,如表格和短段落。较小的分块可以让您更精确地检索具体剂量或禁忌等信息,而不会引入无关上下文。对于需要更多上下文才能正确理解的叙述性部分,较大的分块效果更好。
在上传文档时配置分块参数:
operation = client.file_search_stores.upload_to_file_search_store(
file="metformin.pdf",
file_search_store_name=file_search_store.name,
config={
"display_name": "metformin",
"chunking_config": {
"white_space_config": {
"max_tokens_per_chunk": 200,
"max_overlap_tokens": 20
}
}
}
)
chunking_config 参数控制 File Search 如何拆分文档。max_tokens_per_chunk 指定每个分块的最大大小,而 max_overlap_tokens 则决定相邻分块之间的重叠内容量。该重叠可确保跨越分块边界的信息不会在检索中丢失。
权衡点很重要:更短的分块带来更精确的检索,但可能错失更广的上下文;更大的分块保留更多语义,但也可能包含无关信息。
对于具有清晰章节边界的技术文档,建议使用较小分块(150-250 词元)。对于研究论文或报告等叙述性文档,较大分块(400-600 词元)有助于保留论证脉络与上下文。请参阅 官方 File Search 文档,获取针对不同文档类型选择分块大小的更多指导。
元数据过滤
当存储区包含数十甚至数百份文档时,元数据过滤可在语义搜索运行之前收窄检索范围,从而提高精度并减少处理时间。
在上传文档时添加元数据,以便后续进行过滤:
operation = client.file_search_stores.upload_to_file_search_store(
file="metformin.pdf",
file_search_store_name=file_search_store.name,
config={
"display_name": "metformin",
"custom_metadata": [
{"key": "category", "string_value": "diabetes"},
{"key": "year", "numeric_value": 2017},
{"key": "drug_class", "string_value": "biguanide"}
]
}
)
custom_metadata 参数接受键值对数组。对于类别或药物类别等文本元数据使用 string_value,对于年份、版本或其他数值数据使用 numeric_value。
通过元数据过滤执行只针对相关文档的搜索:
query = "What are the common side effects?"
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=query,
config=types.GenerateContentConfig(
tools=[
types.Tool(
file_search=types.FileSearch(
file_search_store_names=[file_search_store.name],
metadata_filter="category=diabetes"
)
)
]
)
)
metadata_filter 参数将检索限制为匹配特定条件的文档。在此示例中,File Search 仅考虑 category=diabetes 的文档,忽略存储区中存在的降压和降胆固醇药物文档。
当存储区包含异构文档时,这一点尤为关键。一个医疗知识库可能包括药品说明书、研究论文和临床指南。按文档类型过滤可确保您从说明书而非研究摘要中获取剂量信息。
您可以将元数据过滤与完整的语义搜索能力结合使用。过滤会先运行以选出候选文档,然后语义搜索会在这些文档中寻找最相关的段落。
性能优化
File Search 的性能取决于存储区大小、查询复杂度以及模型选择。遵循以下指南有助于保持检索速度并控制成本。
存储区大小限制:将单个存储区控制在 20GB 以内有助于获得更理想的检索时延。File Search 会与文档一起存储嵌入,嵌入的体积大约是原文件的三倍。索引后,7GB 的 PDF 集合大约会产出 21GB 的存储数据,超过建议上限。
当接近该上限时,请按类别、时间段或访问模式创建独立存储区。以医疗助手为例,与其将所有可用药物索引在一个存储区,不如为不同药物类别分别创建存储区。
成本结构:File Search 对索引按每百万词元 $0.15 收费。完成索引后,您可以进行数千次查询而无额外索引费用。该定价模式更适合读多写少的工作负载,即反复查询相同文档的场景。
模型选择:大多数查询使用 gemini-2.5-flash。其处理时延为 1-2 秒,成本显著低于 gemini-2.5-pro。将 gemini-2.5-pro 留给需要跨多来源进行深度推理或处理极其复杂综合任务的查询。在高并发应用中,模型间的成本差异往往比索引成本更为重要。
随着您添加文档,请监控存储区大小。您可以通过 API 检查,不过大小计算在 Google 后端完成,上传后可能不会立即反映。有关完整技术规范与限制,请参阅 Gemini File API 文档。
管理多个存储区
每个 Google Cloud 项目最多支持 10 个 File Search 存储区。多个存储区可以按访问控制、性能需求或逻辑组织进行隔离。
为不同用例创建专用存储区:
# Create separate stores for different document categories
diabetes_store = client.file_search_stores.create(
config={"display_name": "diabetes-medications"}
)
cardio_store = client.file_search_stores.create(
config={"display_name": "cardiovascular-medications"}
)
在一次请求中查询多个存储区:
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="What medications treat both diabetes and heart disease?",
config=types.GenerateContentConfig(
tools=[
types.Tool(
file_search=types.FileSearch(
file_search_store_names=[
diabetes_store.name,
cardio_store.name
]
)
)
]
)
)
File Search 会从所有指定的存储区检索并综合结果。基于来源的元数据会标识每条引用来自哪个存储区,确保完整的可追溯性。
存储区会永久保留,需要在不再使用时手动删除,适用于需要跨会话与部署保持可查询状态的生产级应用。
接下来,您将了解 File Search 与其他 RAG 方案的比较,以及何时选择托管方案或自建方案。
Google 文件搜索工具 vs 其他文件搜索与 RAG 工具
File Search 并非构建 RAG 应用的唯一选项。了解它与替代方案的对比有助于您选择合适的工具。下面将对比 Google 的方法、OpenAI 的方案以及传统自定义构建。
|
功能 |
Google File Search |
OpenAI File Search |
自定义 RAG(LangChain) |
|
定价模式 |
$0.15/百万词元(仅索引) |
0.10/GB 每日存储 |
基础设施 + 开发成本 |
|
分块控制 |
自动化并提供基础配置 |
可配置(默认 800 词元,重叠 400) |
对策略拥有完全控制 |
|
检索类型 |
语义(仅向量) |
混合(向量 + 关键词) |
取决于您实施的方法 |
|
文件格式 |
150+ 类型(PDF、DOCX、代码等) |
6 种类型(TXT、MD、HTML、DOCX、PPTX、PDF) |
取决于所用解析器 |
|
搭建时间 |
数分钟 |
数分钟 |
数天至数周 |
|
引用 |
内置并附带基于来源的元数据 |
内置 |
需自行实现 |
|
最佳适用场景 |
高查询量、快速部署 |
以关键词为主的查询、适度可控 |
复杂需求、完全定制 |
Google File Search vs OpenAI File Search
两家公司都提供托管式 RAG,但在定价与能力上路线不同。
定价: Google 在索引阶段一次性收费(每千次查询 $2.50,外加每日每 GB $0.10 的存储)。如果您查询频繁而更新不多,Google 的模式更省钱;若频繁重索引,计算会更复杂。
配置控制: Google 通过自动化分块与有限配置保持简洁;OpenAI 则提供更多控制。您可以设置分块大小(默认 800 词元)与重叠(400 词元)。OpenAI 还会运行将向量与关键词匹配结合的混合检索,而 Google 纯依赖语义搜索。当查询包含特定技术术语或产品编码时,这一点尤为重要。
文件格式: Google 支持 150+ 文件类型,包括代码文件及多种文档格式。OpenAI 支持六种:TXT、MD、HTML、DOCX、PPTX 和 PDF。两者都不擅长处理 CSV 或 JSONL 等结构化数据,此时自定义方案更具优势。
集成: Google 与 Gemini 模型及 Google Cloud 服务集成;OpenAI 则与其模型家族及 Azure 集成。两者均提供引用与来源跟踪。
核心分歧在于简洁与可控之间的取舍。Google 将一切封装为一次 API 调用;OpenAI 允许您调优检索,但代价是更高复杂度。不存在单一赢家,取决于您的项目更需要速度还是自定义能力。
自定义 RAG 的能力
使用 LangChain 等工具自建 RAG 系统可解锁托管服务不具备的能力。DataCamp 的 RAG with LangChain 课程对此方法进行了详细讲解。
自定义方案可实现以下高级技术:
- 语义切分,在主题切换处智能分段,而非固定长度切割
- 词元感知分块,精确适配模型上下文窗口
- 混合检索,将 BM25 关键词搜索与稠密向量相结合
- 查询变换(如 HyDE),通过生成假设答案改进搜索
- 图谱 RAG,将文档表示为实体与关系的网络
DataCamp 关于 提升 RAG 性能 的教程通过示例展示了这些技术带来的可测量质量提升。权衡之处在于运维复杂度:您需要监控数据库性能、调优嵌入模型,并在多个服务间处理更新。
何时使用各类方案
在以下情况下选择托管工具(如 File Search):
- 构建原型或概念验证,速度优先
- 用例符合标准范式(基于文档的问答、知识库、文档搜索)
- 团队缺乏深厚的 RAG 专业能力
- 希望获得可预测成本与最小运维负担
在以下情况下选择自建:
- 需要高级分块或专门的检索方法
- 处理结构化数据或非常规文件格式
- 构建 agentic RAG 系统,将多种策略相结合
- 在超大规模下通过工程投入优化成本
- 合规性要求特定基础设施或模型
多数项目会先从托管方案起步,只有在需求驱动时才转向自建。自定义 RAG 的诸多技术(智能分块、混合搜索、查询优化)仍可指导您如何使用托管工具。了解全景有助于您在需求演进中做出更优选择。
结语
您已使用 Google 文件搜索工具构建了一个完整的 RAG 系统,从索引 FDA 药品说明书到进行带引用的查询。该医疗助手展示了托管服务如何处理基础设施,而您专注于应用逻辑。
当您需要可靠的 RAG、又不想管理向量数据库或嵌入管道时,File Search 表现出色。免费的存储与查询嵌入让成本可预测。持久化存储区消除了重索引开销,使您能够在不增加运维的前提下扩展查询量。
在部署到生产之前,请补充本教程为简洁而省略的关键保障措施。为上传操作实现带超时的错误处理,并在 API 调用外层添加 try-catch。将文档上传至 Google 服务器时,要考虑数据隐私,尤其是敏感内容。访问引用前先验证基于来源的元数据是否存在。与领域专家充分测试,以发现即使有来源支撑、模型仍可能生成似是而非答案的情况。
下一步,尝试通过元数据过滤按类别组织文档,并根据文档类型尝试不同分块大小。无论是构建支持机器人、文档搜索还是知识助手,您在此学到的技术都适用。
FAQs
文档变更后需要重新索引吗?
需要。更新或替换文件必须重新上传,才能使嵌入反映最新内容。
我能控制对特定文档的访问吗?
目前还不支持细粒度级别。您可以使用元数据过滤来限制可被查询的文档,但暂不支持用户级权限。
文件与存储区的大小限制是什么?
单个文件最大约 100 MB,存储区分层最高约 1 TB。将存储区保持在 20 GB 以下通常可实现更快检索。
引用的可靠性如何?
File Search 会附带基于来源的元数据,显示哪些文档分块为答案提供支撑。这些引用可提升透明度,但仍需审核其准确性。
File Search 使用关键词还是向量检索?
它依赖语义(基于向量)的搜索。若您需要精确的关键词匹配,则需要自定义或混合检索方案。
我是一名数据科学内容创作者,拥有超过 2 年的经验,并在 Medium 上拥有较大的读者群。我喜欢用略带讽刺的笔调撰写有关 AI 和机器学习的深度文章——毕竟总得想点办法让这些内容不那么枯燥。我已发表 130 多篇文章,并制作了一门 DataCamp 课程,另有一门正在筹备中。我的内容已被超过 500 万人次阅读,其中有 2 万人在 Medium 和 LinkedIn 上成为关注者。
