3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Foundry IQ(Azure AI Search)の新機能 Reflection:反復検索による精度向上

Last updated at Posted at 2025-12-11

Microsoft Ignite 2025 では、Azure AI Search の Agentic retrieval 機能がさらにアップデートされました。特に検索パイプラインの L3 の部分に Semantic Classifier が追加され、Microsoft が独自にチューニングした SLM が検索結果が質問の回答結果として妥当かどうか判断し、妥当でない場合はもう一度プランを修正して再検索する機能が実装されました。

image.png

最初の検索が実行された後、 高精度の Semantic Classifier は取得したドキュメントを評価して、さらなる処理と L3 ランク付けが必要かどうかを判断します。最初のパスからの最初の結果がクエリに十分な関連性を持っていない場合は、修正されたクエリプランを使用してフォローアップイテレーションが実行されます。この変更されたクエリプランでは、以前の結果が考慮され、クエリの微調整、用語の拡大、Web などの他のナレッジソースの追加によって反復処理が行われます。

検索推論努力プロパティ

Agentic Retrieval では、クエリプランニングと回答作成のための大規模言語モデル(LLM)処理のレベルを指定できます。この retrievalReasoningEffort プロパティを Medium にセットすることで Reflection (反復)検索を有効化できます。

image.png

Medium によって有効化される Semantic classifier は以下の処理を実行します。

  • 質問に答えるのに十分な文脈があることを認識します
  • コンテキストの既存の情報を使用して、不十分な結果を再試行します。新しいクエリでは詳細に絞り込んだり、検索の幅を広げたりする場合があります。応答のアクティビティログには、より包括的な回答に使用される生成されたクエリが表示されます
  • L3 Classifier を用いて再スコアリングを行います。範囲は L2 ランク付けと同じです。絶対範囲は 0 ~ 4.0 です。

再試行は 1 回だけです。 各イテレーションでは待機時間とコストが追加されるため、システムは再試行を 1 回のパスに制限します。2 回目のイテレーションでは、クエリパイプラインに入力トークンが追加され、課金対象の入力トークンの合計数が合計されます。

イテレーションでは、さまざまなソースを再利用または選択できます。2 回目のパスは、不足している情報を提供するために最も有望なナレッジリソースを選択します。

Web グラウンディングを利用したい場合、別途 Web Knowledge Source を追加する必要があります。

Agentic retrieval の Python 実装

pip install azure-search-documents==11.7.0b2 以降で利用可能です。ナレッジソースおよびナレッジベースは Azure Portal からも作成できます。

image.png

0. Agentic retrieval 用のインデックスを作成

特に注意: Agentic retrieval で使用されるインデックスには、次の要素が必要です。

  • searchable および retrievable として属性付けされた文字列フィールド
  • セマンティック構成および defaultSemanticConfiguration の設定

1. ナレッジソースの作成

以前に作成したインデックスを対象とするナレッジソースを作成します

from azure.identity import DefaultAzureCredential, get_bearer_token_provider
from azure.search.documents.indexes.models import SearchIndex, SearchField, VectorSearch, VectorSearchProfile, HnswAlgorithmConfiguration, AzureOpenAIVectorizer, AzureOpenAIVectorizerParameters, SemanticSearch, SemanticConfiguration, SemanticPrioritizedFields, SemanticField, SearchIndexKnowledgeSource, SearchIndexKnowledgeSourceParameters, SearchIndexFieldReference, KnowledgeBase, KnowledgeBaseAzureOpenAIModel, KnowledgeSourceReference, KnowledgeRetrievalOutputMode, KnowledgeRetrievalLowReasoningEffort, KnowledgeRetrievalMediumReasoningEffort
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents import SearchIndexingBufferedSender
from azure.search.documents.knowledgebases import KnowledgeBaseRetrievalClient
from azure.search.documents.knowledgebases.models import KnowledgeBaseRetrievalRequest, KnowledgeBaseMessage, KnowledgeBaseMessageTextContent, SearchIndexKnowledgeSourceParams


knowledge_source_name = "wikipedia-knowledge-source"

# Create a knowledge source
ks = SearchIndexKnowledgeSource(
    name=knowledge_source_name,
    description="Wikipedia の知識ソース",
    search_index_parameters=SearchIndexKnowledgeSourceParameters(
        search_index_name=index_name,
        source_data_fields=[SearchIndexFieldReference(name="id"), SearchIndexFieldReference(name="page_number")]
    ),
)

credential = DefaultAzureCredential()

index_client = SearchIndexClient(endpoint=endpoint, credential=credential)
index_client.create_or_update_knowledge_source(knowledge_source=ks)
print(f"Knowledge source '{knowledge_source_name}' created or updated successfully.")

インデックスと 1:1 対応します。さらにインデックスだけでなく対応する外部・内部データソースも設定できます。11.7.0b2 では source_data_fields が反映されなかったので Azure Portal から設定しました。

ナレッジソースの取得

knowledge_source = index_client.get_knowledge_source(knowledge_source_name)
knowledge_source.serialize()

2. ナレッジベースの作成

ナレッジベースはナレッジソースと LLM を包み込む役割を果たします。EXTRACTIVE_DATA は既定で生成的な変更を加えずにナレッジソースからコンテンツを返します。Foundry Agent Service との連携にはこれが推奨されます。


knowledge_base_name = "assistant-knowledge-base"

#  Create a knowledge base
aoai_params = AzureOpenAIVectorizerParameters(
    resource_url=aoai_endpoint,
    deployment_name=aoai_gpt_deployment,
    model_name=aoai_gpt_model,
)

knowledge_base = KnowledgeBase(
    name=knowledge_base_name,
    description = "このナレッジベースは、関連性のない2つのサンプルインデックスに向けられた質問を扱います。",
    models=[KnowledgeBaseAzureOpenAIModel(azure_open_ai_parameters=aoai_params)],
    knowledge_sources=[
        KnowledgeSourceReference(
            name=knowledge_source_name
        )
    ], # Add More KnowledgeSourceReference
    output_mode=KnowledgeRetrievalOutputMode.EXTRACTIVE_DATA, # Choose ANSWER_SYNTHESIS or EXTRACTIVE_DATA
    answer_instructions="質問に対して適切な回答を簡潔に提供してください。",
    retrieval_instructions="宿泊先に関する問い合わせにはホテルの知識ソースを利用し、それ以外の場合はWikipediaナレッジソースを利用してください。",
)

index_client = SearchIndexClient(endpoint=search_endpoint, credential=credential)
index_client.create_or_update_knowledge_base(knowledge_base)
print(f"Knowledge base '{knowledge_base_name}' created or updated successfully.")

ナレッジソース:ナレッジベース=N:1 の関係になります。様々なナレッジソースを追加しましょう。

モデルに依存するナレッジベースのパラメーター

  • description: ナレッジベースの説明。 LLM はこの記述を使用してクエリ計画を通知します。

  • retrieval_instructions: ナレッジソースがクエリのスコープ内にある必要があるかどうかを LLM が判断するためのプロンプト。複数のナレッジ ソースがある場合に推奨されます。このフィールドは、ナレッジ ソースの選択とクエリの作成の両方に影響します。たとえば、指示によって情報が追加されたり、ナレッジソースに優先順位が付けられたりする場合があります。指示は LLM に直接渡され、基本的なナレッジソースをバイパスする指示など、クエリ計画を中断する指示を提供できます。

  • answer_instructions: 合成された回答を整形するためのカスタム命令。 既定値は null。

ナレッジベースの取得

knowledge_base = index_client.get_knowledge_base(knowledge_base_name)
knowledge_base.serialize()

KnowledgeBase の作成で指定する KnowledgeBaseAzureOpenAIModel のパラメーターが AzureOpenAIVectorizerParameters なのはちょっと混乱しますね。指定するのは Embeddings モデルではなく gpt-4o, gpt-4o-mini, gpt-4.1-nano, gpt-4.1-mini, gpt-4.1, gpt-5, gpt-5-mini, gpt-5-nano である必要があります。

3. Messages の定義

検索クエリーではなく、KnowledgeBaseMessage の形式でリクエストすることができ、以下のようなチャット応答をそのまま検索コンテキストとして考慮できます。

instructions = """
あなたはあらゆる質問に回答できるFAQエージェント。
ソースはJSON形式で、回答内に引用する必要があるref_idを含んでいます。
回答が分からない場合は「分かりません」と回答してください。
"""

messages = [
    {
        "role": "system",
        "content": instructions
    }
]

今回はシンプルな質問のみなので上記は使用していません。

4. 検索の実行

このステップでは Agentic retrieval パイプラインを実行し、根拠に基づく引用付き回答を生成します。会話履歴と検索パラメータに基づき、ナレッジベースは以下を実行します。

  1. 会話全体を分析し、ユーザーの情報ニーズを推測
  2. 複合クエリを焦点の絞られたサブクエリに分解
  3. ナレッジソースに対してサブクエリを並行して実行
  4. セマンティックランカーを使用して結果を再ランク付けし、フィルタリング
  5. 上位の結果を自然言語の回答に統合(ANSWER_SYNTHESIS の場合)
import json
import textwrap

agent_client = KnowledgeBaseRetrievalClient(endpoint=endpoint, knowledge_base_name=knowledge_base_name, credential=credential)

messages.append({
    "role": "user",
    "content": """
平清盛の生まれた場所はどこですか?
   """
})

req = KnowledgeBaseRetrievalRequest(
    messages=[
        KnowledgeBaseMessage(
            role=m["role"],
            content=[KnowledgeBaseMessageTextContent(text=m["content"])]
        ) for m in messages if m["role"] != "system"
    ],
    knowledge_source_params=[
        SearchIndexKnowledgeSourceParams(
            knowledge_source_name=knowledge_source_name,
            include_references=True,
            include_reference_source_data=True,
            always_query_source=True,
        )
    ],
    include_activity=True,
    retrieval_reasoning_effort=KnowledgeRetrievalMediumReasoningEffort()
)

retrieval_result = agent_client.retrieve(retrieval_request=req)
print("Response")
print(textwrap.fill(retrieval_result.response[0].content[0].text, width=120))

注意:retrieval_reasoning_effort パラメータは、クラス自体ではなくそのクラスのインスタンスオブジェクトを期待しているため、() をつけてインスタンス化する必要があります。

5. 結果の評価

References:

references 配列は、基になるグラウンドデータからの直接参照であり、応答の生成に使用される sourceData が含まれています。これは Agentic retrieval エンジンによって検出され、セマンティックランキングされたすべての単一のドキュメントで構成されます。

print("## References:")
if retrieval_result.references:
    print(json.dumps([r.as_dict() for r in retrieval_result.references], indent=2, ensure_ascii=False))
else:
    print("No references found on 'result'")
[
  {
    "type": "searchIndex",
    "id": "0",
    "activity_source": 2,
    "source_data": {
      "docid": "133865#3",
      "text": "永久6年1月18日(1118年2月10日)、伊勢平氏の頭領である平忠盛の長男として生まれる(実父は白河法皇という説もある。詳細後述)。出身地は京都府京都市という説が有力である。生母は不明だが、もと白河法皇に仕えた女房で、忠盛の妻となった女性(『中右記』によると保安元年(1120年)没)である可能性が高い。『平家物語』の語り本系の諸本は清盛の生母を祇園女御としているが、読み本系の延慶本は清盛は祇園女御に仕えた中﨟女房の腹であったというように書いている。また、近江国胡宮神社文書(『仏舎利相承系図』)は清盛生母を祇園女御の妹とし、祇園女御が清盛を猶子としたと記している。清盛が忠盛の正室の子でない(あるいは生母が始め正室であったかもしれないがその死後である)にもかかわらず嫡男となった背景には、後見役である祇園女御の権勢があったとも考えられる。",
      "title": "平清盛",
      "AzureSearch_DocumentKey": "aHR0cHM6Ly9zd..."
    },
    "reranker_score": 3.5596642,
    "doc_key": "aHR0cHM6Ly9zd..."
  },
  ...

Activity:

activity 配列はクエリープランを出力します。これによりリクエストの実行時に行われた操作を追跡するのに役立ちます。また、リソース呼び出しの課金への影響と頻度を理解できるように、運用上の透明性も提供されます。

import json
print("## Activity: ")
print(json.dumps([a.as_dict() for a in retrieval_result.activity], indent=2, ensure_ascii=False))
[
  {
    "id": 0,
    "type": "modelQueryPlanning",
    "elapsed_ms": 3727,
    "input_tokens": 1500,
    "output_tokens": 135
  },
  {
    "id": 1,
    "type": "searchIndex",
    "elapsed_ms": 1089,
    "knowledge_source_name": "wikipedia-knowledge-source",
    "query_time": "2025-12-10T04:24:27.408Z",
    "count": 9,
    "search_index_arguments": {
      "search": "平清盛 生まれた場所",
      }
...
  {
    "id": 2,
    "type": "searchIndex",
    "search_index_arguments": {
      "search": "平清盛 出身地",      

...
  {
    "id": 5,
    "type": "modelQueryPlanning",
    "elapsed_ms": 9050,
    "input_tokens": 5659,
    "output_tokens": 341
  },
...
  {
    "id": 6,
    "type": "searchIndex",
    "search_index_arguments": {
      "search": "平清盛 生誕地 史料 京都説 根拠",
...
  {
    "id": 7,
    "type": "agenticReasoning",
    "reasoning_tokens": 49793,
    "retrieval_reasoning_effort": {
      "kind": "medium"
    }

modelQueryPlanning が2回走っていることが確認できますね。agenticReasoningretrieval_reasoning_effort.kindmedium になっていることも確認できます。

精度評価

いつも使っている MIRACL 日本語データセットを用いて Recall を測定します。

条件

Embeddings model: text-embedding-3-large
dimension: 3,072
k_nearest_neighbors: 5
top: 5
ナレッジベースに接続したナレッジソース: MIRACL 8,066 件を格納したインデックス1つのみ。Web Knowledge Source は使用していない。

image.png

私は数年に渡り、Azure AI Search の新機能が出る度に同じデータセットを用いて定点観測してきました。今回、Agentic retrieval with Reflection により初めて Recall で 9割の精度に達しました。retrieval_reasoning_effort(検索推論努力)は low よりも medium のほうが精度が高かったため、L3 Classifier および Reflection の効果による精度向上が確認できたことになります。

これは素晴らしいことだと思います。皆さんもぜひ検証を行ってみてください。ただし精度は当然データセットに依存しますし、新機能では LLM を用いますので使用するモデルの性能やプロンプトにも影響を受ける点は注意してください。あとパフォーマンスについても Reflection 処理やモデルの推論時間を追加で考慮する必要があります。

使用するモデルごとの精度

image.png

今回は精度評価のため EXTRACTIVE_DATA を使用しているため、LLM はソース選定、クエリー計画、Reflection のみに使用されます。現在対応している LLM は gpt-4o, gpt-4o-mini, gpt-4.1-nano, gpt-4.1-mini, gpt-4.1, gpt-5, gpt-5-mini, gpt-5-nano となりますので比較的新しめのモデルで精度を比較しています。gpt-5 系のモデルが精度が高く出ました。モデルおよびプロンプトを変えてみて検証を行ってください。

今回は厳密に1つのナレッジソースだけで行いましたが、さらに複数のナレッジソースを追加した場合も気になりますね。(条件が変わってしまうのでやっていませんが)

GitHub

参考

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?