「複数のドキュメントを読んで要点をまとめてくれ」という依頼が来るたびに、何時間も費やしてきた経験はないでしょうか。PDFを開いて、画像の文字を読み取って、テキストファイルを確認して……この繰り返し作業こそ、Gemini 2.5 Pro が最も得意とする領域です。
なぜ Gemini 2.5 Pro なのか
ドキュメント解析ツールを作る際に最初に直面する壁は「どのモデルを選ぶか」です。2026年4月時点で、Gemini 2.5 Pro を選ぶ理由は明確です。
まずコンテキストウィンドウ。Gemini 2.5 Pro は最大100万トークンのコンテキストを扱えるため、数十ページのPDFや大量の画像を一度に渡せます。GPT-4o の128KやClaude Sonnetの200Kと比べて相応の優位性があります。
次にネイティブマルチモーダル。PDF・画像・音声・動画を追加の変換なしに直接入力できます。従来のアプローチ(pdfplumberでテキスト抽出→pytesseractでOCR→APIに送信)と異なり、ページレイアウトや図表の視覚的な文脈ごとモデルに渡せます。
最後に精度と速度のバランス。Thinking モードをオフにすれば高速・低コスト、複雑な推論が必要な場面では thinking_budget を設定するという柔軟な制御が可能です。
実装の全体設計
今回構築するシステムの流れを先に整理しておきます。
入力ファイル群(PDF / JPG / PNG / TXT / MD)
↓
File API へアップロード(並列処理)
↓
Gemini 2.5 Pro で一括解析
↓
Markdownレポート出力
ポイントは「File API を経由する」という点です。1MBを超えるファイルや複数ファイルを効率よく扱うには、インラインデータ(base64埋め込み)よりもFile APIが適しています。アップロードしたファイルは48時間有効なため、同じドキュメントセットを複数の観点で解析する際の再利用も可能です。
環境セットアップ
まず必要なパッケージをインストールします。
pip install google-genai pathlib2google-genai は2025年以降の公式推奨パッケージです。旧来の google-generativeai から移行済みの方はそのまま利用できますが、APIの呼び出し方が異なるため注意してください。
環境変数にAPIキーを設定します。
export GOOGLE_API_KEY="YOUR_GEMINI_API_KEY"Google AI Studio(aistudio.google.com)でキーを発行し、プロジェクトへのアクセスが有効であることを確認してください。
Step 1: File API へのアップロード処理
import os
import time
import concurrent.futures
from pathlib import Path
from google import genai
from google.genai import types
client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
SUPPORTED_MIME = {
".pdf": "application/pdf",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
".txt": "text/plain",
".md": "text/markdown",
}
def upload_file(file_path: Path) -> types.File | None:
"""単一ファイルをFile APIへアップロードする。
戻り値: アップロード済みFileオブジェクト(失敗時はNone)
"""
suffix = file_path.suffix.lower()
mime = SUPPORTED_MIME.get(suffix)
if mime is None:
print(f" ⚠ スキップ(未対応形式): {file_path.name}")
return None
try:
uploaded = client.files.upload(
file=file_path,
config=types.UploadFileConfig(mime_type=mime),
)
# アップロード完了まで待機(処理中ステータスの場合)
while uploaded.state == "PROCESSING":
time.sleep(2)
uploaded = client.files.get(name=uploaded.name)
if uploaded.state == "FAILED":
print(f" ✗ アップロード失敗: {file_path.name}")
return None
print(f" ✓ アップロード完了: {file_path.name} ({mime})")
return uploaded
except Exception as e:
print(f" ✗ エラー: {file_path.name} — {e}")
return None
def upload_files_parallel(file_paths: list[Path], max_workers: int = 4) -> list[types.File]:
"""複数ファイルを並列アップロードする。"""
print(f"\n📤 {len(file_paths)}件のファイルをアップロード中...")
results = []
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(upload_file, p): p for p in file_paths}
for future in concurrent.futures.as_completed(futures):
result = future.result()
if result is not None:
results.append(result)
print(f"✅ {len(results)}件のアップロード完了\n")
return resultsconcurrent.futures.ThreadPoolExecutor で並列アップロードを実現しています。ファイルが10件あっても逐次処理より約4倍高速になります。max_workers=4 はAPIのレート制限(毎分アップロード数)を超えないよう抑えた値です。大量ファイルを扱う場合は time.sleep() を挿入して調整してください。
Step 2: Gemini 2.5 Pro による一括解析
ANALYSIS_PROMPT = """
以下のドキュメント群を解析し、Markdown形式でレポートを作成してください。
## レポート要件
1. **エグゼクティブサマリー**(200文字程度)— 全体の要点を一言でまとめる
2. **ドキュメント別要約** — 各ファイルの主要な情報を箇条書きで整理する
3. **重要な数値・データ** — 表・グラフ・数値情報を抽出してまとめる
4. **キーインサイト** — 複数のドキュメントを横断した共通テーマや矛盾点を指摘する
5. **アクションアイテム** — 次のステップとして推奨される具体的なアクション(あれば)
## 注意事項
- 図表やレイアウト情報も含めて解析してください
- 日本語のドキュメントは日本語で、英語は英語で要約してください
- 事実を正確に記述し、推測の場合は明示してください
"""
def analyze_documents(uploaded_files: list[types.File]) -> str:
"""アップロード済みファイルをGemini 2.5 Proで一括解析する。
戻り値: Markdownレポート文字列
"""
# ファイル参照をコンテンツリストに変換
contents: list = [ANALYSIS_PROMPT]
for f in uploaded_files:
contents.append(
types.Part.from_uri(file_uri=f.uri, mime_type=f.mime_type)
)
print("🤖 Gemini 2.5 Pro で解析中...")
response = client.models.generate_content(
model="gemini-2.5-pro-preview-05-06",
contents=contents,
config=types.GenerateContentConfig(
temperature=0.2, # レポートなので低め(事実重視)
max_output_tokens=8192,
# thinking_budget=2000, # 複雑な推論が必要なら有効化
),
)
if not response.text:
raise ValueError("モデルからの応答が空です。ファイルの内容を確認してください。")
return response.text
def save_report(markdown_text: str, output_path: Path) -> None:
"""レポートをMarkdownファイルとして保存する。"""
output_path.write_text(markdown_text, encoding="utf-8")
print(f"📄 レポート保存: {output_path}")temperature=0.2 は重要な設定です。ドキュメント要約・事実抽出の用途では創造性より正確性が求められます。0に近いほど安定したアウトプットになりますが、完全な0にするとやや機械的な文体になるため、0.2程度が実務的なバランスです。
Step 3: メイン処理とアップロード済みファイルのクリーンアップ
def cleanup_uploaded_files(uploaded_files: list[types.File]) -> None:
"""使用済みのアップロードファイルをFile APIから削除する。
File APIのファイルは48時間後に自動削除されるが、
機密情報を含む場合は即時削除することを推奨する。
"""
for f in uploaded_files:
try:
client.files.delete(name=f.name)
except Exception:
pass # 削除失敗は無視(自動削除で対応)
print(f"🗑 {len(uploaded_files)}件のアップロードファイルを削除しました")
def main(input_dir: str, output_file: str = "report.md") -> None:
"""メイン処理。
Args:
input_dir: 解析するファイルが入ったディレクトリパス
output_file: 出力するMarkdownレポートのファイル名
"""
input_path = Path(input_dir)
if not input_path.is_dir():
raise FileNotFoundError(f"ディレクトリが見つかりません: {input_dir}")
# 対応ファイルの収集
file_paths = [
p for p in input_path.iterdir()
if p.is_file() and p.suffix.lower() in SUPPORTED_MIME
]
if not file_paths:
print("解析対象のファイルが見つかりませんでした。")
return
print(f"📁 解析対象: {len(file_paths)}件")
for p in file_paths:
print(f" - {p.name}")
uploaded_files = []
try:
# Step 1: 並列アップロード
uploaded_files = upload_files_parallel(file_paths)
if not uploaded_files:
print("有効なファイルがアップロードされませんでした。")
return
# Step 2: 一括解析
markdown_report = analyze_documents(uploaded_files)
# Step 3: レポート保存
output_path = Path(output_file)
save_report(markdown_report, output_path)
print("\n✅ 処理完了!")
finally:
# 機密情報保護のため確実にクリーンアップ
if uploaded_files:
cleanup_uploaded_files(uploaded_files)
if __name__ == "__main__":
import sys
input_dir = sys.argv[1] if len(sys.argv) > 1 else "./documents"
output_file = sys.argv[2] if len(sys.argv) > 2 else "report.md"
main(input_dir, output_file)try/finally ブロックで確実にクリーンアップを実行するのがポイントです。エラーが発生した場合でもアップロードファイルが残り続けることを防ぎます。とくに契約書・財務資料など機密性の高いドキュメントを処理する場合は、48時間の自動削除を待たず即時削除することを強くお勧めします。
実行例と出力
以下のファイル構成でテストした場合:
documents/
├── Q1_report.pdf # 四半期財務レポート(英語)
├── architecture.png # システム構成図
└── meeting_notes.md # 会議メモ(日本語)
実行コマンド:
python document_assistant.py ./documents report_20260415.md期待される出力(コンソール):
📁 解析対象: 3件
- Q1_report.pdf
- architecture.png
- meeting_notes.md
📤 3件のファイルをアップロード中...
✓ アップロード完了: Q1_report.pdf (application/pdf)
✓ アップロード完了: architecture.png (image/png)
✓ アップロード完了: meeting_notes.md (text/markdown)
✅ 3件のアップロード完了
🤖 Gemini 2.5 Pro で解析中...
📄 レポート保存: report_20260415.md
✅ 処理完了!
🗑 3件のアップロードファイルを削除しました
生成されるMarkdownレポートには、財務指標の抽出・アーキテクチャ図の説明・会議の決定事項が統合された形でまとめられます。
よくあるエラーとその対処法
RESOURCE_EXHAUSTED: Quota exceeded
無料プランのFile APIは1日あたりのアップロード上限があります。大量ファイルを扱う場合は有料プラン(Tier 1以上)へのアップグレード、またはtime.sleep(60)を挿入してリクエストを分散させてください。
INVALID_ARGUMENT: The File API does not support this MIME type
.docx(Word文書)や.xlsx(Excel)は直接アップロードできません。
File state is FAILED
破損したPDFや暗号化(パスワード保護)されたファイルはアップロードに失敗します。pikepdfやPyMuPDFで事前にファイルを検証・修復する処理を追加することを検討してください。
応答が空になる(response.text が None)
安全フィルターによってブロックされた可能性があります。response.prompt_feedback を確認し、フィルタリング理由を把握してください。フィルタリングを回避するためにプロンプトを調整するか、必要に応じてsafety_settingsを変更してください。
プロンプトのカスタマイズ例
ANALYSIS_PROMPT は用途に応じて変更するだけで、大幅に異なるレポートを生成できます。
法務ドキュメントレビュー用:
LEGAL_PROMPT = """
以下の契約書・法律文書を解析し、以下の観点でレポートを作成してください:
1. 当事者・締結日・有効期間
2. 主要な義務と権利
3. リスク条項(違約金・免責・守秘義務)
4. 不明確な表現・要確認事項
※ 法的判断ではなく情報整理として活用してください。
"""競合分析レポート用:
COMPETITIVE_PROMPT = """
添付の資料(プレスリリース・製品仕様書・SNS投稿等)を解析し:
1. 各社の最新動向と発表内容
2. 機能・価格の比較マトリクス
3. 市場ポジションの変化
4. 自社への示唆とリスク
をまとめてください。
"""全体を振り返って
Gemini 2.5 Pro の File API とマルチモーダル能力を組み合わせることで、複数形式のドキュメントを一括解析してレポートを自動生成するシステムを、100行程度のPythonで構築できます。
次のステップとして、Gemini API のストリーミング機能を組み合わせて、解析進捗をリアルタイムに表示する対話型UIを追加するのがお勧めです。また、システムインストラクションの活用でモデルのキャラクターを固定すれば、チーム全体で一貫したレポートスタイルを維持できます。