GEMINI LABEN
SIRI — WWDC 2026で刷新版SiriがGoogle Geminiモデルで動くと確定。ただしEUではDMAによりiOS 27時点で提供されませんFLASH3.5 — Gemini 3.5 FlashがGA。エージェント・コーディングで持続的なフロンティア性能を発揮する最上位FlashモデルですIMAGE-GA — Gemini 3.1 Flash Image / 3.1 Pro Imageがネイティブ視覚モデルとしてGA。preview版は6/25に終了予定MANAGED-AGENTS — Gemini APIでManaged Agentsが公開プレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを構築できますFILE-SEARCH — File Searchがマルチモーダル対応。gemini-embedding-2で画像のネイティブ埋め込み・検索が可能になりましたDEPRECATION — gemini-3.1-flash-image-preview / gemini-3-pro-image-previewは6/25に停止。GA版への移行をお早めにSIRI — WWDC 2026で刷新版SiriがGoogle Geminiモデルで動くと確定。ただしEUではDMAによりiOS 27時点で提供されませんFLASH3.5 — Gemini 3.5 FlashがGA。エージェント・コーディングで持続的なフロンティア性能を発揮する最上位FlashモデルですIMAGE-GA — Gemini 3.1 Flash Image / 3.1 Pro Imageがネイティブ視覚モデルとしてGA。preview版は6/25に終了予定MANAGED-AGENTS — Gemini APIでManaged Agentsが公開プレビュー。Googleホストの隔離Linuxサンドボックスで自律エージェントを構築できますFILE-SEARCH — File Searchがマルチモーダル対応。gemini-embedding-2で画像のネイティブ埋め込み・検索が可能になりましたDEPRECATION — gemini-3.1-flash-image-preview / gemini-3-pro-image-previewは6/25に停止。GA版への移行をお早めに
記事一覧/高度な活用
高度な活用/2026-04-14上級

Gemma 4 で RAG システムを構築する——ローカルLLMとベクトル検索を組み合わせた実践アーキテクチャ

Gemma 4 の256Kコンテキストと Apache 2.0 ライセンスを活用した RAG(Retrieval-Augmented Generation)システムの設計・実装ガイド。ChromaDB、pgvector との統合、チャンキング戦略、プロダクション最適化まで完全解説。

gemma-410rag22vector-search7chromadb2pgvector4local-llm2production90

「ローカルLLMでRAGを動かしたいが、Ollama で動かした Gemma 4 と、別途用意したベクトルデータベースをどう繋ぐか分からない」——この悩みは、オープンソースLLMを本番環境に持ち込む際に必ずぶつかる壁です。

ここではGemma 4 と ChromaDB または pgvector を組み合わせた RAG システムの実装を、アーキテクチャ設計から本番最適化まで段階的に解説します。Apache 2.0 ライセンスのコンポーネントで固めているため、商用サービスへの組み込みに制約がありません。

RAG と Gemma 4 の組み合わせが強い理由

従来の RAG 実装では「コンテキスト窓が短い→チャンクを細かく切る→文脈が失われる」というトレードオフが常につきまとっていた。Gemma 4 の 31B Dense と 26B MoE は 256K トークンのコンテキスト窓を持ち、E2B/E4B でも 128K あります。これは、RAG の設計思想そのものを変え得るスペックです。

具体的には、検索でヒットしたチャンクを複数まとめて1回のプロンプトに含める「Long-Context RAG」が現実的な選択肢になります。100件のチャンク(各1,000トークン)を一度に渡しても、256K 窓には余裕があります。モデルが文書間の関係を直接参照しながら回答を生成できるため、チャンクの境界で文脈が切れる問題が大幅に軽減されます。

もう一つの強みが Apache 2.0 ライセンスです。Gemma 3 では商用利用に制約のあるカスタムライセンスが使われていたが、Gemma 4 は完全な Apache 2.0 に移行しました。これは SaaS への組み込み、OEM 提供、派生モデルの商業販売がすべて自由になることを意味します。

システムアーキテクチャの全体設計

本番 RAG システムの構成要素は次の5層からなります。

┌─────────────────────────────────────────┐
│ 1. ドキュメント処理層                    │
│    PDF/HTML/Markdown → テキスト抽出     │
│    → チャンキング → 埋め込み生成         │
├─────────────────────────────────────────┤
│ 2. ベクトルストア層                      │
│    ChromaDB(開発・中規模)              │
│    pgvector(PostgreSQL統合・大規模)    │
├─────────────────────────────────────────┤
│ 3. 検索層                               │
│    セマンティック検索 + キーワード検索   │
│    ハイブリッドランキング(RRF)         │
├─────────────────────────────────────────┤
│ 4. 生成層                               │
│    Gemma 4(Ollama / Gemini API)        │
│    Long-Context or Chunked RAG           │
├─────────────────────────────────────────┤
│ 5. キャッシュ・最適化層                  │
│    Redis(クエリキャッシュ)             │
│    バッチ埋め込み生成                    │
└─────────────────────────────────────────┘

Step 1: 埋め込みモデルの選択とセットアップ

Gemma 4 自体は汎用生成モデルであり、直接埋め込みベクトルを出力する設計ではありません。RAG では専用の埋め込みモデルと組み合わせて使う。

Gemma 4 との相性が良い埋め込みモデルの選択肢:

# オプション1: multilingual-e5-large(多言語対応・ローカル実行可能)
from sentence_transformers import SentenceTransformer
 
embedding_model = SentenceTransformer("intfloat/multilingual-e5-large")
# 1024次元、50以上の言語に対応、Apache 2.0
 
# オプション2: text-embedding-3-small(Google Gemini API)
import google.generativeai as genai
genai.configure(api_key="YOUR_GEMINI_API_KEY")
# Google の埋め込みAPIを使う場合(Gemma 4 との同一エコシステム)
 
# オプション3: nomic-embed-text(Ollama経由でローカル実行)
import requests
 
def embed_with_ollama(text: str) -> list:
    resp = requests.post(
        "http://localhost:11434/api/embeddings",
        json={"model": "nomic-embed-text", "prompt": text}
    )
    return resp.json()["embedding"]

プロダクションでは multilingual-e5-large と Ollama の nomic-embed-text を比較検証することを勧める。前者は品質が高く、後者はローカル実行で外部依存がありません。

Step 2: ChromaDB との統合

ChromaDB は開発環境や中規模用途(数百万件程度のドキュメント)に最適なベクトルストアです。Python ネイティブな API と永続化機能を持ちます。

import chromadb
from sentence_transformers import SentenceTransformer
from chromadb.utils import embedding_functions
import uuid
 
# ChromaDB セットアップ
client = chromadb.PersistentClient(path="./chroma_db")
embedding_fn = embedding_functions.SentenceTransformerEmbeddingFunction(
    model_name="intfloat/multilingual-e5-large"
)
 
collection = client.get_or_create_collection(
    name="documents",
    embedding_function=embedding_fn,
    metadata={"hnsw:space": "cosine"}
)
 
def add_documents(texts: list, metadatas: list = None):
    ids = [str(uuid.uuid4()) for _ in texts]
    collection.add(
        documents=texts,
        metadatas=metadatas or [{}] * len(texts),
        ids=ids
    )
    return ids
 
def search_documents(query: str, n_results: int = 10, filter_meta: dict = None):
    results = collection.query(
        query_texts=[query],
        n_results=n_results,
        where=filter_meta
    )
    return [
        {"text": doc, "metadata": meta, "distance": dist}
        for doc, meta, dist in zip(
            results["documents"][0],
            results["metadatas"][0],
            results["distances"][0]
        )
    ]
 
# 使用例
add_documents(
    texts=["Gemma 4は2026年4月にGoogleが発表したオープンソースLLMです。"],
    metadatas=[{"source": "gemma4_overview.pdf", "page": 1}]
)
results = search_documents("Gemma 4の発表時期は?")

Step 3: pgvector との統合(大規模・PostgreSQL統合)

既存の PostgreSQL インフラに AI 機能を追加したい場合は pgvector が最適です。既存の RDB との JOIN も可能なため、ユーザーデータと知識ベースを組み合わせた高度なクエリが書ける。

import psycopg2
import numpy as np
from sentence_transformers import SentenceTransformer
 
model = SentenceTransformer("intfloat/multilingual-e5-large")
 
class PgVectorRAG:
    def __init__(self, conn_string: str):
        self.conn = psycopg2.connect(conn_string)
        self._setup_schema()
 
    def _setup_schema(self):
        with self.conn.cursor() as cur:
            cur.execute("CREATE EXTENSION IF NOT EXISTS vector;")
            cur.execute("""
                CREATE TABLE IF NOT EXISTS documents (
                    id SERIAL PRIMARY KEY,
                    content TEXT NOT NULL,
                    embedding vector(1024),
                    metadata JSONB,
                    created_at TIMESTAMP DEFAULT NOW()
                );
            """)
            # IVFFlat インデックス(大規模データ向け)
            cur.execute("""
                CREATE INDEX IF NOT EXISTS documents_embedding_idx
                ON documents USING ivfflat (embedding vector_cosine_ops)
                WITH (lists = 100);
            """)
            self.conn.commit()
 
    def add_document(self, content: str, metadata: dict = None):
        embedding = model.encode(content).tolist()
        with self.conn.cursor() as cur:
            cur.execute(
                "INSERT INTO documents (content, embedding, metadata) VALUES (%s, %s, %s)",
                (content, embedding, psycopg2.extras.Json(metadata or {}))
            )
            self.conn.commit()
 
    def search(self, query: str, k: int = 10, min_similarity: float = 0.7) -> list:
        query_embedding = model.encode(query).tolist()
        with self.conn.cursor() as cur:
            cur.execute("""
                SELECT content, metadata,
                       1 - (embedding <=> %s::vector) AS similarity
                FROM documents
                WHERE 1 - (embedding <=> %s::vector) > %s
                ORDER BY embedding <=> %s::vector
                LIMIT %s;
            """, (query_embedding, query_embedding, min_similarity, query_embedding, k))
            return [
                {"content": row[0], "metadata": row[1], "similarity": float(row[2])}
                for row in cur.fetchall()
            ]
 
# 使用例
rag = PgVectorRAG("postgresql://user:password@localhost:5432/ragdb")
rag.add_document("Gemma 4のMoEモデルは推論時に3.8Bパラメータがアクティブになる。")
results = rag.search("Gemma 4 MoEのアクティブパラメータ数")

Step 4: Gemma 4 と組み合わせた生成

検索結果を Gemma 4 に渡して回答を生成します。256K コンテキストを活かした Long-Context RAG では、複数の検索結果を1回のプロンプトにまとめて送れます。

import requests
 
def generate_answer_gemma4(
    query: str,
    context_chunks: list,
    model: str = "gemma4:27b",
    use_long_context: bool = True
) -> str:
    if use_long_context:
        # Long-Context RAG: チャンクをすべて1プロンプトに
        context_text = "
 
---
 
".join([
            f"[出典: {c.get("metadata", {}).get("source", "不明")}]
{c["text"]}"
            for c in context_chunks
        ])
        prompt = f"""以下のドキュメント群を参照して質問に答えてください。
 
# 参照ドキュメント
{context_text}
 
# 質問
{query}
 
# 回答(参照した出典を明示してください)"""
    else:
        # 通常RAG: 上位チャンクのみ
        top_context = "
 
".join([c["text"] for c in context_chunks[:3]])
        prompt = f"コンテキスト:
{top_context}
 
質問: {query}
 
回答:"
 
    response = requests.post(
        "http://localhost:11434/api/generate",
        json={
            "model": model,
            "prompt": prompt,
            "stream": False,
            "options": {
                "temperature": 0.1,  # RAGでは低温度で事実に基づく回答を促す
                "top_p": 0.9
            }
        }
    )
    return response.json()["response"]
 
def rag_pipeline(query: str, vector_store, k: int = 20) -> str:
    # 検索
    results = vector_store.search(query, k=k)
    # 生成
    answer = generate_answer_gemma4(query, results)
    return answer

Step 5: ハイブリッド検索とクエリ拡張

セマンティック検索だけでは漏れが生じます。キーワード検索と組み合わせた「ハイブリッド検索」と、クエリを言い換えて検索範囲を広げる「クエリ拡張」で精度を上げる。

from rank_bm25 import BM25Okapi
import re
 
class HybridSearchRAG:
    def __init__(self, vector_rag, documents: list, metadatas: list):
        self.vector_rag = vector_rag
        self.documents = documents
        # BM25 インデックス構築
        tokenized = [re.findall(r"\w+", doc.lower()) for doc in documents]
        self.bm25 = BM25Okapi(tokenized)
        self.metadatas = metadatas
 
    def search(self, query: str, k: int = 10, alpha: float = 0.5) -> list:
        # セマンティック検索結果
        semantic_results = {
            r["text"]: r["similarity"]
            for r in self.vector_rag.search(query, k=k*2)
        }
 
        # BM25 検索結果
        query_tokens = re.findall(r"\w+", query.lower())
        bm25_scores = self.bm25.get_scores(query_tokens)
        max_bm25 = max(bm25_scores) if max(bm25_scores) > 0 else 1.0
        bm25_results = {
            self.documents[i]: bm25_scores[i] / max_bm25
            for i in bm25_scores.argsort()[-k*2:][::-1]
        }
 
        # Reciprocal Rank Fusion (RRF) でスコアを統合
        all_docs = set(semantic_results.keys()) | set(bm25_results.keys())
        fused_scores = {
            doc: alpha * semantic_results.get(doc, 0) + (1 - alpha) * bm25_results.get(doc, 0)
            for doc in all_docs
        }
 
        top_k = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)[:k]
        return [{"text": doc, "score": score} for doc, score in top_k]

プロダクション最適化:バッチ埋め込みとキャッシュ

大量のドキュメントを処理する場合、埋め込み生成をバッチ化することで処理時間が大幅に短縮されます。

import redis
import hashlib
import json
from sentence_transformers import SentenceTransformer
 
model = SentenceTransformer("intfloat/multilingual-e5-large")
redis_client = redis.Redis(host="localhost", port=6379, db=0)
 
def get_embeddings_cached(texts: list, cache_ttl: int = 86400) -> list:
    embeddings = []
    texts_to_encode = []
    cache_keys = []
 
    # キャッシュ確認
    for text in texts:
        key = f"emb:{hashlib.md5(text.encode()).hexdigest()}"
        cached = redis_client.get(key)
        if cached:
            embeddings.append(json.loads(cached))
            texts_to_encode.append(None)
        else:
            embeddings.append(None)
            texts_to_encode.append(text)
        cache_keys.append(key)
 
    # キャッシュミスをバッチエンコード
    uncached_texts = [t for t in texts_to_encode if t is not None]
    if uncached_texts:
        # batch_size=64でGPUメモリを効率利用
        new_embeddings = model.encode(uncached_texts, batch_size=64).tolist()
        idx = 0
        for i, text in enumerate(texts_to_encode):
            if text is not None:
                embeddings[i] = new_embeddings[idx]
                redis_client.setex(cache_keys[i], cache_ttl, json.dumps(new_embeddings[idx]))
                idx += 1
 
    return embeddings

どのユースケースにこの構成が最適か

Gemma 4 + ChromaDB/pgvector の RAG 構成が最も効果を発揮するのは次のような場面です。

社内ドキュメント検索システム——外部 API に送れない機密文書(法的書類、財務データ、医療記録)を社内サーバーで完全オフライン処理できます。Apache 2.0 ライセンスなので商用利用に追加費用がかからありません。

多言語ナレッジベース——Gemma 4 の140言語サポートと多言語埋め込みモデルを組み合わせることで、日英混在の技術ドキュメントや多言語カスタマーサポートに対応できます。

コードベース検索——256K コンテキストを活用して、大規模なコードベース全体を一度に参照しながら実装方針を問い合わせられます。チャンキングが不要なため、関数をまたいだ依存関係も正しく理解されます。

まず ollama pull gemma4:27b でモデルを取得し、ChromaDB のローカル環境で検証してから pgvector 移行を検討する順序が現実的です。

シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

高度な活用2026-04-21
Gemma 4 on MLX 本番運用チューニング — 量子化・コンテキスト・Reasoning 代替戦略
MLX 版 Gemma 4 をローカル推論のバックボーンに据えるための、量子化ビット選定・コンテキスト戦略・Reasoning 機能欠落の補い方を実装コード込みで解説します。Gemini API との役割分担も扱います。
高度な活用2026-03-28
TurboQuantをRAG・ベクトル検索に応用する — KVキャッシュ圧縮技術の新しい活用法
Google TurboQuantの圧縮技術はLLM推論だけでなく、RAGパイプラインのベクトルデータベースにも応用可能です。埋め込みベクトルの圧縮によるメモリ効率化、検索速度向上、大規模RAGシステムへの実装アプローチを解説します。
API / SDK2026-04-19
Gemini APIで作るRAGシステム実装ガイド:埋め込みから本番デプロイまで
Gemini APIのEmbedding APIとGemini 2.5 ProをベースにしたRAGシステムの設計・実装・本番デプロイを完全解説。ベクトルストア選定、チャンク戦略、ハルシネーション対策、パフォーマンス最適化まで網羅します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →