GEMINI LABEN
PRO35 — Gemini 3.5 Proの報道上の目標日である7月17日を迎えましたが、本日時点でGoogleの公式発表もモデルカードの公開も確認できていませんUNCONFIRMED — 2Mトークンのコンテキストも価格もGoogleは未確認のままです。Vertex AIでの限定的なエンタープライズプレビューにとどまると報じられていますFLASH35 — 一般提供中の最新はgemini-3.5-flash。gemini-flash-latestが指すモデルもこちらに切り替わっていますAGENTS — Gemini APIのManaged Agentsが公開プレビューに。Googleがホストする隔離Linuxサンドボックス上で、状態を持つ自律エージェントを動かせますSEARCH — File Searchがマルチモーダル検索に対応。gemini-embedding-2で画像をそのまま埋め込んで検索できますWEBHOOKS — Batch APIと長時間実行オペレーションのポーリングを置き換える、イベント駆動のWebhookが追加されましたPRO35 — Gemini 3.5 Proの報道上の目標日である7月17日を迎えましたが、本日時点でGoogleの公式発表もモデルカードの公開も確認できていませんUNCONFIRMED — 2Mトークンのコンテキストも価格もGoogleは未確認のままです。Vertex AIでの限定的なエンタープライズプレビューにとどまると報じられていますFLASH35 — 一般提供中の最新はgemini-3.5-flash。gemini-flash-latestが指すモデルもこちらに切り替わっていますAGENTS — Gemini APIのManaged Agentsが公開プレビューに。Googleがホストする隔離Linuxサンドボックス上で、状態を持つ自律エージェントを動かせますSEARCH — File Searchがマルチモーダル検索に対応。gemini-embedding-2で画像をそのまま埋め込んで検索できますWEBHOOKS — Batch APIと長時間実行オペレーションのポーリングを置き換える、イベント駆動のWebhookが追加されました
記事一覧/高度な活用
高度な活用/2026-07-17上級

日本語で引いた検索に、対訳の英語記事が出てこない — 埋め込みが内容より先に言語を見ている件

gemini-embedding-2 で日英の対訳ペアを埋め込むと、同じ内容なのに最近傍になりません。言語そのものが類似度を押し上げているためです。対訳ペアを正解として再現率を測り、言語重心を引く方法と翻訳ブリッジを比べた記録をまとめます。

Gemini API188gemini-embedding-27多言語6ベクトル検索3評価設計

プレミアム記事

4つのサイトで日本語と英語の記事を必ずペアで公開しています。片方だけ存在すると言語切替で 404 になるため、日英は常に同数です。手元の1サイトでいま JA 725 本・EN 725 本。つまり「意味が同じで言語だけが違う文書」が 725 組、正解つきで揃っている状態です。

この対を使って、公開前の重複検知を作ろうとしました。新しく書いた日本語の下書きを埋め込み、既存記事の中から近いものを引く。日英どちらの既存記事も同じインデックスに入れておけば、言語をまたいだ重複も拾えるはずでした。

引いてみると、上位10件が全部日本語記事でした。私自身、最初はインデックスの作り方を間違えたのだと思いました。しかも、その日本語の下書きには必ず対応する英語版が存在しているにもかかわらず、その対訳は上位に出てきません。内容が完全に一致する相手が、内容の遠い同言語の記事に負けています。

task_type は揃えていました。正規化もしていました。原因はもっと手前にありました。

コサイン類似度は「何語で書かれているか」も測っている

埋め込みモデルは、渡した文をベクトルにします。そのベクトルには意味だけが入っていてほしいのですが、実際には書かれた言語という属性も一緒に載ります。

多言語モデルは、確かに言語をまたいで意味を近づけるように学習されています。ただ「まったく同じ向きに揃える」ようには学習されていません。結果として、埋め込み空間の中で日本語の文はゆるやかに一箇所に集まり、英語の文は別の一箇所に集まります。

比較対象意味の近さ言語の一致実際の類似度
日本語の下書き ↔ その英語対訳ほぼ同一不一致
日本語の下書き ↔ 別テーマの日本語記事遠い一致中〜高

言語の一致が稼ぐ下駄が、意味の差を埋めてしまう。上位10件が日本語で占められていたのは、モデルが間違えたからではありませんでした。私が「意味だけを測っているつもりで、言語も一緒に測っていた」だけです。

この成分をどう扱うかは用途で変わります。同一言語内の検索しかしないなら、言語成分は全ベクトルに等しく載るので順位に影響しません。困るのは、日本語と英語を同じインデックスに混ぜて引く瞬間だけです。私はそこを踏みました。

対訳ペアは、そのまま評価データセットになる

先に測り方を用意します。対訳ペアの良いところは、正解のラベル付けが要らないことです。gemini-3-multitool-agent-guide.mdx の日本語版から引いたとき、正解は同じ slug の英語版だと最初から決まっています。

100 組ほどあれば傾向は見えます。次のコードは、日英ペアを埋め込んで JA→EN の再現率と MRR を出す最小の計測台です。

import os, json, math, random
from google import genai
from google.genai import types
 
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
MODEL = "gemini-embedding-2"
 
def embed(texts, task_type):
    """テキスト群を埋め込む。API 側の上限に合わせて分割する。"""
    out = []
    for i in range(0, len(texts), 32):
        chunk = texts[i:i + 32]
        res = client.models.embed_content(
            model=MODEL,
            contents=chunk,
            config=types.EmbedContentConfig(task_type=task_type),
        )
        out.extend([e.values for e in res.embeddings])
    return out
 
def l2_normalize(v):
    n = math.sqrt(sum(x * x for x in v))
    return [x / n for x in v] if n else v
 
def cosine(a, b):
    return sum(x * y for x, y in zip(a, b))
 
# pairs: [{"slug": "...", "ja": "日本語本文の冒頭2000字", "en": "English body ..."}, ...]
pairs = json.load(open("pairs.json"))
random.seed(42)
pairs = random.sample(pairs, min(100, len(pairs)))
 
ja_vecs = [l2_normalize(v) for v in embed([p["ja"] for p in pairs], "RETRIEVAL_QUERY")]
en_vecs = [l2_normalize(v) for v in embed([p["en"] for p in pairs], "RETRIEVAL_DOCUMENT")]
 
def evaluate(queries, docs):
    """queries[i] の正解は docs[i]。順位を集計する。"""
    hits = {1: 0, 5: 0, 10: 0}
    rr_sum = 0.0
    for i, q in enumerate(queries):
        scored = sorted(
            ((cosine(q, d), j) for j, d in enumerate(docs)),
            reverse=True,
        )
        rank = next(r for r, (_, j) in enumerate(scored, start=1) if j == i)
        for k in hits:
            if rank <= k:
                hits[k] += 1
        rr_sum += 1.0 / rank
    n = len(queries)
    return {
        "recall@1": hits[1] / n,
        "recall@5": hits[5] / n,
        "recall@10": hits[10] / n,
        "MRR": rr_sum / n,
    }
 
print("baseline:", evaluate(ja_vecs, en_vecs))

この時点では、母集団に英語文書しか入っていません。言語の下駄は全候補に等しくかかるので、ここでの再現率は素直に出ます。私の手元では recall@1 が 0.9 前後、つまり 90% ほどの下書きで対訳が1位に来ていました。

問題は次です。候補に日本語記事も混ぜます。

# 日本語記事も同じインデックスに入れる(自分自身は候補から除く)
ja_docs = [l2_normalize(v) for v in embed([p["ja"] for p in pairs], "RETRIEVAL_DOCUMENT")]
 
def evaluate_mixed(queries, en_docs, ja_docs):
    hits = {1: 0, 5: 0, 10: 0}
    for i, q in enumerate(queries):
        cands = [(cosine(q, d), ("en", j)) for j, d in enumerate(en_docs)]
        cands += [(cosine(q, d), ("ja", j)) for j, d in enumerate(ja_docs) if j != i]
        cands.sort(reverse=True)
        rank = next(r for r, (_, key) in enumerate(cands, start=1) if key == ("en", i))
        for k in hits:
            if rank <= k:
                hits[k] += 1
    n = len(queries)
    return {f"recall@{k}": v / n for k, v in hits.items()}
 
print("mixed:", evaluate_mixed(ja_vecs, en_vecs, ja_docs))

日本語候補を混ぜた途端に recall@1 が崩れました。混ぜる前と後の差が、そのまま言語成分の大きさです。単一言語のベンチマークで問題なく見えていたインデックスが、多言語にした瞬間に別物になる。この落差を数字にできたことが、この計測台の一番の収穫でした。

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

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
対訳ペアという「正解が既知の集合」を使って、自分のインデックスの言語横断の再現率を今日測れる
言語重心を引くだけで追加のAPI呼び出しゼロのまま言語横断の順位を立て直す実装が手に入る
重心除去・翻訳ブリッジ・言語別インデックスの三択を、遅延とコストの軸で選び分けられる
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

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

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

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

関連記事

高度な活用2026-07-15
分類ラベルの近ミスはリトライで直らない — 閉じた語彙を正規化する層を挟んだ実測ノート
responseSchema の enum を外したラベルをリトライで直そうとすると同じ近ミスが返ります。壁紙アプリの30分類バッチで外れ方の分布を測り、別名表と埋め込み最近傍で正規化する受け入れ層を挟んだ実装と実測値をまとめました。
高度な活用2026-07-10
カテゴリを30から34に増やした日 — 8,000枚を全部かけ直さない差分再分類の設計
分類カテゴリを追加したとき、既存資産を全件かけ直す必要はありません。埋め込みで影響ラベルを絞り、確信度マージンで境界資産を拾う差分再分類の実装と実測値をまとめました。
高度な活用2026-06-01
Gemini Embedding の次元を 3072 から 768 へ切り詰める — ベクトルDBのコストとレイテンシを下げる Matryoshka 設計
gemini-embedding-001 は 3072 次元の埋め込みを返しますが、Matryoshka 表現のおかげで前方だけを切り出しても精度がほとんど落ちません。次元を 768 へ削ってベクトルDBのストレージとレイテンシを下げる設計を、再正規化の落とし穴と粗密二段検索のコード付きで組み立てます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →