取り組みの背景 — なぜGemini × Pineconeなのか
RAG(Retrieval-Augmented Generation)は、LLMに外部ナレッジを与えて回答精度を高める技術として、今や多くのプロダクトに採用されています。その中核となるのがベクターデータベースです。
数あるベクターDBの中でもPineconeは、マネージドサービスとして完全クラウド運用できる手軽さが魅力です。一方、Gemini Embedding APIは最大3,072次元のテキスト埋め込みを生成でき、日本語を含む多言語で高い検索精度を発揮します。
この2つを組み合わせることで、サーバー管理不要・スケーラブルなRAGシステムをすばやく構築できます。ここでは環境準備からクエリ最適化、本番運用のポイントまで、実際に動くPythonコードとともに解説します。
前提知識と環境準備
必要なアカウントとキー
作業を始める前に以下を用意してください。
- Google AI Studio のAPIキー(aistudio.google.com から無料取得可能)
- Pinecone のAPIキー(app.pinecone.io でフリープランから利用可能)
ライブラリのインストール
pip install google-generativeai pinecone-client python-dotenv.env ファイルを作成してキーを管理します。
# .env
GOOGLE_API_KEY=YOUR_GOOGLE_API_KEY
PINECONE_API_KEY=YOUR_PINECONE_API_KEYPineconeインデックスの作成
Pineconeのインデックス(コレクション)を作成する際、次元数をEmbeddingモデルに合わせることが最重要です。gemini-embedding-004 の次元数は 768 です。
import os
from dotenv import load_dotenv
from pinecone import Pinecone, ServerlessSpec
load_dotenv()
# Pineconeクライアント初期化
pc = Pinecone(api_key=os.environ["PINECONE_API_KEY"])
INDEX_NAME = "gemini-rag-index"
# インデックスが存在しない場合のみ作成
if INDEX_NAME not in pc.list_indexes().names():
pc.create_index(
name=INDEX_NAME,
dimension=768, # gemini-embedding-004 の次元数
metric="cosine", # コサイン類似度で検索
spec=ServerlessSpec(
cloud="aws",
region="us-east-1" # フリープランで利用可能なリージョン
)
)
print(f"✅ インデックス '{INDEX_NAME}' を作成しました")
else:
print(f"✅ インデックス '{INDEX_NAME}' は既に存在します")
index = pc.Index(INDEX_NAME)ドキュメントの埋め込みとインデックス登録
Gemini Embedding APIで埋め込みを生成する
import google.generativeai as genai
load_dotenv()
genai.configure(api_key=os.environ["GOOGLE_API_KEY"])
def embed_texts(texts: list[str], task_type: str = "RETRIEVAL_DOCUMENT") -> list[list[float]]:
"""
テキストリストをGemini Embedding APIで埋め込みに変換する。
task_type:
RETRIEVAL_DOCUMENT — インデックス登録時(ドキュメント側)
RETRIEVAL_QUERY — 検索クエリ側
"""
result = genai.embed_content(
model="models/gemini-embedding-004",
content=texts,
task_type=task_type
)
# result["embedding"] は texts の長さと同じリストを返す
return result["embedding"]task_type を使い分けるのがポイントです。ドキュメント登録時は RETRIEVAL_DOCUMENT、検索時は RETRIEVAL_QUERY を指定することで、検索精度が向上します。
ドキュメントをPineconeに登録する
import hashlib
import json
def upsert_documents(docs: list[dict], index, batch_size: int = 50) -> None:
"""
docs: [{"id": "...", "text": "...", "metadata": {...}}, ...]
バッチサイズ50件ずつPineconeにアップサート
"""
for i in range(0, len(docs), batch_size):
batch = docs[i : i + batch_size]
texts = [d["text"] for d in batch]
embeds = embed_texts(texts, task_type="RETRIEVAL_DOCUMENT")
vectors = [
{
"id": d["id"],
"values": embed,
"metadata": {**d.get("metadata", {}), "text": d["text"]}
}
for d, embed in zip(batch, embeds)
]
index.upsert(vectors=vectors)
print(f" 登録済: {i + len(batch)}/{len(docs)} 件")
# 使用例
sample_docs = [
{
"id": "doc-001",
"text": "Gemini 2.5 Proは2026年3月にリリースされたGoogleの最新AIモデルです。",
"metadata": {"source": "news", "lang": "ja"}
},
{
"id": "doc-002",
"text": "Pineconeはサーバーレスアーキテクチャのベクターデータベースで、スケールアウトが容易です。",
"metadata": {"source": "docs", "lang": "ja"}
},
{
"id": "doc-003",
"text": "RAG(Retrieval-Augmented Generation)は検索とLLMを組み合わせて回答精度を上げる手法です。",
"metadata": {"source": "tutorial", "lang": "ja"}
},
]
upsert_documents(sample_docs, index)
# 期待出力: 登録済: 3/3 件RAGパイプラインの実装
セマンティック検索でコンテキストを取得する
def search_context(query: str, index, top_k: int = 5) -> list[dict]:
"""
クエリに関連するドキュメントをPineconeから取得する。
戻り値: [{"text": ..., "score": ..., "metadata": ...}, ...]
"""
# クエリ用埋め込みを生成
query_embed = embed_texts([query], task_type="RETRIEVAL_QUERY")[0]
results = index.query(
vector=query_embed,
top_k=top_k,
include_metadata=True
)
contexts = []
for match in results["matches"]:
contexts.append({
"text": match["metadata"].get("text", ""),
"score": match["score"],
"metadata": match["metadata"]
})
return contextsGeminiに検索結果を渡して回答を生成する
model = genai.GenerativeModel("gemini-2.5-flash")
def answer_with_rag(question: str, index, top_k: int = 5) -> str:
"""
RAGパイプライン全体: 検索 → コンテキスト構築 → 生成
"""
# 1. セマンティック検索
contexts = search_context(question, index, top_k=top_k)
if not contexts:
return "関連するドキュメントが見つかりませんでした。"
# 2. コンテキストをプロンプトに組み込む
context_text = "\n\n".join(
f"[参考{i+1}(類似度: {c['score']:.3f})]\n{c['text']}"
for i, c in enumerate(contexts)
)
prompt = f"""以下の参考情報をもとに、質問に正確かつ簡潔に答えてください。
参考情報に含まれない内容は「情報がありません」と答えてください。
## 参考情報
{context_text}
## 質問
{question}
"""
response = model.generate_content(prompt)
return response.text
# 使用例
answer = answer_with_rag("Gemini 2.5 Proとは何ですか?", index)
print(answer)
# 期待出力: Gemini 2.5 Proは2026年3月にリリースされたGoogleの最新AIモデルです。本番運用での最適化ポイント
メタデータフィルタリングで検索精度を上げる
Pineconeはメタデータを使った絞り込みに対応しています。大量のドキュメントを扱う場合、まずメタデータでフィルタリングしてからベクター検索すると精度と速度が向上します。
# 日本語ドキュメントだけを対象に検索する例
filtered_results = index.query(
vector=query_embed,
top_k=5,
filter={"lang": {"$eq": "ja"}}, # メタデータフィルター
include_metadata=True
)チャンキング戦略でRAG精度を上げる
長文ドキュメントをそのままEmbeddingすると、重要な情報が希薄になります。実運用ではチャンク分割が必要です。
def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
"""
テキストをオーバーラップ付きでチャンク分割する。
chunk_size: 1チャンクの最大文字数
overlap: 前後チャンクとの重複文字数(文脈の連続性を保つため)
"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start = end - overlap # オーバーラップ分だけ戻る
return chunksPineconeフリープランのコスト管理
フリープランでは 5つのインデックス と 2GBのストレージ が利用できます。本番環境での費用管理のポイントをまとめます。
- 不要なベクターを定期的に削除:
index.delete(ids=["old-doc-001"])で個別削除 - バッチ処理で登録コストを下げる: 1件ずつ
upsertするより50〜100件まとめてリクエストする - クエリ頻度の監視: Pineconeダッシュボードで月次クエリ数を確認し、スロットリングを避ける
Gemini Embedding APIのコスト最適化については、Gemini API コンテキストキャッシュ完全ガイド でさらに詳しく解説しています。
まとめ
ここではGemini Embedding API と Pinecone を組み合わせたRAGシステムの構築手順を解説しました。
- Pineconeインデックス作成: 次元数768・cosine類似度で設定
- Embedding生成:
task_typeでドキュメント用とクエリ用を使い分け - バッチ登録: 50件単位でアップサートしてAPI呼び出しを最小化
- メタデータフィルター: 検索対象を絞り込んで精度と速度を向上
- チャンキング: 500文字・50文字オーバーラップで長文の情報密度を確保
さらに高度なRAGパターン(マルチモーダル対応・リランキング・ハイブリッド検索)を学びたい方には、Gemini APIマルチモーダルRAGパイプライン構築ガイド がおすすめです。
Pinecone × Gemini の組み合わせは、ゼロからのベクターDB構築が不要なため、プロトタイプから本番まで迅速に移行できます。ぜひ自社のナレッジベースに適用してみてください。