AzureAISearchRetriever とAzureSearch().as_retriever() どちらを使うべきなのか
Azure でRAG を実施する場合、(cosomos DB 等も使えるが)Azure AI Search に参考情報を格納するのが手っ取り早いだろう。
LangChain で、Azure AI Search に格納した参考情報を取り出す(retriever)実装例を検索してみると、
-
AzureAISearchRetriever()
を使う -
AzureSearch().as_retriever()
を使う
の2パターンあり、LangChain の公式ドキュメントを読んでも両者の違いがよく分からなかったので、調査してみた。
結論
- フルテキスト検索がしたいなら
AzureAISearchRetriever()
。ただし、現状セマンティックランカーは利用不可。 - ベクトル検索・ハイブリッド検索がしたい、セマンティックランカーを利用したいなら
AzureSearch().as_retriever()
。
Azure AI Search の仕様
Azure AI Search で可能なクエリ検索方法は、Azure AI Search のドキュメントに拠れば、以下の3種類。
- フルテキスト検索
- ベクトル検索
- ハイブリッド検索
ここではLangChain は単なるラッパーなので、AzureAISearchRetriever()
とAzureSearch().as_retriever()
のどちらも、上記の3種類のいずれかの検索方法となるはず。
内部コード調査
AzureAISearchRetriever()
こちらは単純。ソースコードを見れば見当がつく。
要は検索用のURL を作って、そこへクエリを検索するようなGET API を叩いているわけである。
def _search(self, query: str) -> List[dict]:
search_url = self._build_search_url(query)
response = requests.get(search_url, headers=self._headers)
if response.status_code != 200:
raise Exception(f"Error in search request: {response}")
return json.loads(response.text)["value"]
ここで実行されているAPI についてAzure のドキュメントで調べてみると、該当するのはDocuments - Search Get だろう。
このドキュメントのsearch
の欄に、
フルテキスト検索クエリ式。すべてのドキュメントに一致するには、"*" を使用するか、このパラメーターを省略します。
と記載されているので、これはフルテキスト検索を実施するためのAPI のようだ。このAPI をラッパーしているAzureAISearchRetriever()
は必然的に、フルテキスト検索しか出来ないことになる。
またこのAPI 自体はセマンティックランカー(semanticConfiguration
)に対応しているが、AzureAISearchRetriever()
が作る検索用のURL はセマンティックランカーを想定していない(search
と$top
と$filter
のみ設定)ので、セマンティックランカーも利用不可である。
AzureSearch().as_retriever()
ドキュメントはこちら。返却されるのはAzureSearchVectorStoreRetriever
なので先程とは別物。
fetch_k
やfilter
の他に、search_type を設定できる。AzureSearch
のドキュメントに拠れば、"similarity" / "hybrid" / "semantic_hybrid" の3通りに対応している。
AzureSearchVectorStoreRetriever
のドキュメントに拠れば、"similarity_score_threshold" / "hybrid_score_threshold" / "semantic_hybrid_score_threshold" も設定可能のはずで、なぜこれらを含めていないのかは謎。- search_type のデフォルト値は“similarity” と記載されているが、langchain-community v0.3.8 では特に処理が実装されていないため、勘違いでなければsearch_type を設定しない場合は
AzureSearchVectorStoreRetriever
側のデフォルト値である"hybrid" になる。修正PR は送信済。
Azure AI Search の仕様と突き合わせると、"similarity" が「ベクトル検索」、"hybrid" が「ハイブリッド検索」に対応しているはずである。(一応裏が取りたくてコード追いかけたが、Azure SDK の領域で断念...)
おまけ
備忘録も兼ねて、検証に使ったプログラムを残しておく。
データベース(インデックス)定義
インデックスが存在しなくても(Azure のGUI で空インデックスを作成しなくても)、自動で作成される。
from langchain_openai import AzureOpenAIEmbeddings
from langchain_community.vectorstores import AzureSearch
index_name = "test-index"
embeddings = AzureOpenAIEmbeddings(
azure_deployment=os.getenv("AZURE_OPENAI_EMBEDDING_MODEL"),
openai_api_key=os.getenv("AZURE_OPENAI_API_KEY"),
openai_api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)
vector_store = AzureSearch(
embedding_function=embeddings.embed_query,
azure_search_endpoint=os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT"),
azure_search_key=os.getenv("AZURE_SEARCH_ADMIN_KEY"),
index_name=index_name
)
ドキュメント格納
vector_store.add_texts(
[
"In 2020, I visited Beijing",
"In 2021, I visited New Orleans",
"In 2022, I visited New York",
"In 2023, I visited Paris",
"In 2024, I visited Tokyo",
]
)
フルテキスト検索
from langchain_community.retrievers import AzureAISearchRetriever
retriever = AzureAISearchRetriever(
content_key="content",
service_name=os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT"),
api_key=os.getenv("AZURE_SEARCH_ADMIN_KEY"),
index_name=index_name
)
retriever.invoke("When did I visited Asia?")
ベクトル検索
retriever = vector_store.as_retriever(search_type="similarity")
retriever.invoke("When did I visited Asia?")
ハイブリッド検索
retriever = vector_store.as_retriever(search_type="hybrid")
retriever.invoke("When did I visited Asia?")
セマンティックランカー利用
Azure のドキュメントに従って、セマンティックランカー("test-semantic")を作成しておくこと。
vector_store = AzureSearch(
embedding_function=embeddings.embed_query,
azure_search_endpoint=os.getenv("AZURE_SEARCH_SERVICE_ENDPOINT"),
azure_search_key=os.getenv("AZURE_SEARCH_ADMIN_KEY"),
index_name=index_name,
semantic_configuration_name="test-semantic",
)
retriever = vector_store.as_retriever(search_type="semantic_hybrid")
retriever.invoke("When did I visited Asia?")
Discussion