RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Pythonで始めるリランカー実装:mixedbread-ai、Alibaba-NLP、OpenAI GPTを比べてみた

mixedbread-ai/ Alibaba-NLP / OpenAI GPTによるリランキング【実装サンプル付き】


はじめに

RAGをはじめとする現代の情報検索システムでは、「リランカー(Reranker)」と呼ばれる仕組みが使われることがあります。

検索候補を単にキーワードマッチやベクトル検索でピックアップするだけでなく、さらに高精度なモデル(=リランカー)で再スコアリング(再ランキング)することで、ユーザーが本当に求めている情報を上位に表示できます。

本記事では、筆者が実際に業務中の検証作業で利用した次の3つのモデル:

  • mixedbread-ai/mxbai-rerank-v2

  • Alibaba-NLP/gte-multilingual

  • OpenAIのGPT(Chatモデルをリランカーとして活用)

を題材に、特徴や実装例を紹介します。


1. リランカーとは?

リランカーは、情報検索の「第二段階目」を担う仕組みです。

  1. 一次検索: BM25などのキーワード検索やベクトル検索で候補文書を多数ピックアップ

  2. リランカーによる再ランキング: 高度な意味理解や言語能力を持つモデルで、一次検索結果を再評価して順位付け

リランカーは、FAQ検索やナレッジベースのチャットボットなど、ユーザー体験の鍵となる部分で利用されています。


2. 各リランカーモデルの特徴

mixedbread-ai/mxbai-rerank-v2

  • 2025年3月に公開された比較的新しめのモデル

  • Apache 2.0で公開されており、商用利用が可能

  • 軽量モデルもありCPU環境でも使いやすい

  • 専用のライブラリ(mxbai_rerank)から手軽に利用できる

Alibaba-NLP/gte-multilingual

  • 中国の大企業Alibaba製

  • Hugging Faceでモデルが公開されており、transformersライブラリから簡単に利用できる

  • Apache 2.0で公開されており、商用利用が可能

  • 70言語以上に対応している(らしい) ※ 日本語と英語に対応していることは確認済

OpenAI GPT(Chatモデルをリランカーとして利用)

  • 専用のライブラリ(opeaai)やlangchainから簡単に利用できる

  • プロンプトによって挙動を柔軟に調整できる

  • 高性能モデルを使う場合、高い精度が期待できる

  • API利用のため運用コスト・レイテンシには注意


3. 実装サンプル

上記3つのモデルを実装してみます。

それぞれのモデルの実装の互換性を保つために最初にデータクラスとリランカーの抽象クラスを実装しておきます。

from abc import ABC, abstractmethod
from pydantic import BaseModel, Field

class RankResult(BaseModel):
    index: int
    score: float
    document: str  =  ""

class BaseReranker(ABC):
    @abstractmethod
    def rerank(self):
        pass
        

mixedbread-ai/mxbai-rerank-v2 を使ったリランカー

from mxbai_rerank import MxbaiRerankV2

class  MxbaiReranker(BaseReranker):
    def __init__(
            self,
            model_name: str = "mixedbread-ai/mxbai-rerank-base-v2",
        ):
        self.model  = MxbaiRerankV2(model_name, device="cpu")

    def rerank(
            self,
            query: str,
            documents: list[str],
            return_documents: bool = True,
            top_n: int = 3,
        ) -> list[RankResult]:
        results  =  self.model.rank(
                        query,
                        documents,
                        return_documents=return_documents,
                        top_k=top_n,
                    )
        return  results

mxbai_reranker  =  MxbaiReranker()

Alibaba-NLP/gte-multilingual を使ったリランカー

import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer

class AlibabaReranker(BaseReranker):
    def  __init__(
            self,
            model_name: str  =  "Alibaba-NLP/gte-multilingual-reranker-base"
        ):
        self.tokenizer  = AutoTokenizer.from_pretrained(model_name)
        self.model  = AutoModelForSequenceClassification.from_pretrained(
                        model_name,
                        trust_remote_code=True,
                        torch_dtype=torch.float32,
                    )
        self.model  =  self.model.to("cpu")
        self.model.eval()

def rerank(
        self,
        query: str,
        documents: list[str],
        return_documents: bool  =  True,
        top_n: int  =  3,
    ) -> list[RankResult]:
    pairs  = [[query, doc] for  doc  in  documents]
    with  torch.no_grad():
        inputs  =  self.tokenizer(pairs, padding=True, truncation=True, return_tensors='pt', max_length=512).to("cpu")
        scores  =  self.model(**inputs, return_dict=True).logits.view(-1, ).float()
    index  =  0
    rank_results  = []
    for p, s in zip(pairs, scores):
        rank_results.append(
            RankResult(
                index=index,
                score=s.item(),
                document=p[1] if  return_documents  else  "",
            )
        )
        index  +=  1
    rank_results.sort(key=lambda  x: x.score, reverse=True)
    return  rank_results[:top_n]

alibaba_reranker = AlibabaReranker()

OpenAI GPT(gpt-4.1-mini)を使ったリランカー

import  os

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

API_KEY  =  "XXXXX"
os.environ["OPENAI_API_KEY"] =  API_KEY

RERANK_PROMPT = """queryとなるテキストと複数のテキストを含むtext_listが与えられます。
text_listの中のテキストとqueryの内容を比較し、queryの内容と近い順にtext_listのテキストを並べ替えなさい。

***条件***
1. queryとの内容が近い順に並べたテキストのインデックスとスコアのリストを返すこと。
2. スコアは0.0から1.0の範囲で、queryとの内容が近いほど高くなるようにすること。
"""

class RerankedIndex(BaseModel):
    index: list[int] = Field(
        default=[],
        description="queryとの内容が近い順に並べたテキストのインデックスのリスト。"
    )
    score: list[float] = Field(
        default=[],
        description="queryとの内容が近い順に並べたテキストのスコアのリスト。"
    )

class OpenaiReranker(BaseReranker):
    def __init__(
            self, 
            model_name: str = "gpt-4.1-mini",
        ):
        self.model  = ChatOpenAI(
                        model=model_name,
                        max_tokens=1000,
                        # temperature=0.0,
                        top_p=0.01,
                        seed=42,
                    )
        self.prompt  = ChatPromptTemplate.from_messages(
            [
                ("system", RERANK_PROMPT),
                ("human", "query: '{query}'\ntext_list: '{text_list}'"),
            ])
        self.chain = self.prompt | self.model.with_structured_output(RerankedIndex)

    def _rerank_text(
            self, 
            query: str,
            text_list: str,
        ) -> int:
        res  =  self.chain.invoke({"query": query, "text_list": text_list})
        return  res

    def rerank(
            self,
            query: str,
            documents: list[str],
            return_documents: bool  =  True,
            top_n: int  =  3,
        ) -> list[RankResult]:
        text_list  = [f"{i}. {doc}"  for  i, doc  in  enumerate(documents)]
        text_list  =  "\n".join(text_list)
        res  =  _rerank_text(query, text_list)
        index_score_pairs  =  list(zip(res.index, res.score))
        rank_results  = []
        for  idx, score  in  index_score_pairs:
            rank_results.append(
                RankResult(
                    index=idx,
                    score=score,
                    document=documents[idx] if  return_documents  else  "",
                )
            )
        rank_results.sort(key=lambda  x: x.score, reverse=True)
        return  rank_results[:top_n]

openai_reranker  =  OpenaiReranker()

リランキングの実行

実行例

test_query  =  "Give me the first document."
test_documents  = [
    "This is the first document.",
    "This is the second document.",
    "This is the third document.",
    "This is the fourth document.",
    "This is the fifth document.",
    ]
res_mxbai = mxbai_reranker.rerank(test_query, test_documents, return_documents=True, top_n=3)
res_gte = alibaba_reranker.rerank(test_query, test_documents, return_documents=True, top_n=3)
res_openai = openai_reranker.rerank(test_query, test_documents, return_documents=True, top_n=3)

4. まとめ

筆者の検証においてリランキングの精度は、

openai (gpt-4.1-mini) > mxbai > alibaba

といった結果になりました。

(精度は対象のテキスト群の内容によるところも大きいと思いますが...)、 実行コストが最も高いopenaiを使ったリランカーで最も良い結果が得られるのは妥当なように思います。

リランカーは検索体験の向上に役立ちます。各モデルの特性やプロダクト要件に応じて、最適な選択を検討してみてください!


参考リンク


Copyright © RAKUS Co., Ltd. All rights reserved.