RAGの回答精度向上――曖昧な質問を最適な検索クエリに変える、Spring AIのTransformerとExpanderを理解するSpring AIで始める生成AIプログラミング(7)

Java×Spring AIで始めるAIプログラミングの入門連載。前回はRAGを使って質問に答える流れを説明しました。今回はRAG内部の処理に着目し、より柔軟な機能拡張を行う際に、どのコンポーネントに注目し、どうカスタマイズすればよいかを説明していきます。

» 2025年11月20日 05時00分 公開

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Spring AIで始める生成AIプログラミング」のインデックス

連載:Spring AIで始める生成AIプログラミング

本連載のサンプルコードをGitHubで公開しています。こちらからダウンロードしてみてください。


RAGシステムを構築する要となるコンポーネント群

 前回は、Spring AIのRAG(検索拡張生成)システムのカスタマイズにおける中心的な存在であるRetrievalAugmentationAdvisor(アドバイザー)の全体像と、その内部で質問から最終的なプロンプトが構築されるまでの流れを紹介しました。アドバイザーが提供する各種モジュールは、RAGシステムを柔軟に設計するための鍵となります。特に、検索の精度を向上させるのに、QueryTransformerやQueryExpanderは重要な存在です。本稿でも、これらのクラスを中心に、具体的な使い方を解説していきます。

使用するサンプルデータについて

 今回のサンプルでも、前回使用した国交省から配布されているマンション規約のひな型文章のデータを用いています。このデータを用いてVectorStoreを作成するには、第5回のサンプルコードを用いて以下のコマンドを実行するだけです。

shell:>elt-extract-pdf --filename 001746766.pdf

 もちろん、前回でデータを作成している人は、この手順は不要です。

質問を最適化する(QueryTransformer)

 QueryTransformerは、RAGにおいて、ユーザーからの質問を変換または修正することで、より関連性の高い検索結果を取得するための仕組みを提供します。表1の実装クラスが用意されています。

クラス 概要
RewriteQueryTransformer 長過ぎる質問や、代名詞などで曖昧な質問を、具体的で検索しやすいクエリに書き換え、最適化
TranslationQueryTransformer 質問を他の言語に翻訳
CompressionQueryTransformer 会話履歴などを考慮した質問などへの変換や、単独で意味が通じるクエリを作成
表1 クエリ最適化のためのTransformerクラス

 これらのクラスは、ユーザーから受け取った初期の質問を検索(ドキュメント取得)に適した形へ変換するため、RAG処理全体において非常に重要な役割を担っています。しかし、これらをパイプライン全体で動かすと、「質問がどのように変換されたか」がLLM(大規模言語モデル)による最終回答の中に吸収されてしまい、「元の質問から何がどう改善されたのか」が把握しにくいという課題があります。

 そこで本稿では、まずRewriteQueryTransformerクラスの内部構造・変換プロンプトに焦点を当てることで、どのような処理が行われているのかを深掘りして見ていきます。

[1]デフォルトのプロンプトを確認する

 RewriteQueryTransformerクラスは「ユーザーからの質問が長過ぎる、曖昧である、あるいはコンテキストが不足している」といった理由で、その後のベクトル検索などが困難になる場合に、検索に適した形に質問を最適化してくれます。状況に合わせた質問に作り直すといった用途でも利用できるでしょう。

 クラス内部で使われるプロンプトは、以下の通りです。

Given a user query, rewrite it to provide better results when querying a {target}.Remove any irrelevant information, and ensure the query is concise and specific.
Original query:
{query}
Rewritten query:
(日本語訳)
与えられたユーザーのクエリを、{target} にクエリを実行した際により良い結果が得られるように書き換えてください。無関係な情報を削除し、クエリが簡潔かつ具体的であることを確認してください。
元のクエリ:{query}
書き換えたクエリ:

 プロンプト確認を通じて、RewriteQueryTransformerがどのような目的を持って動作しているのかを理解しておきましょう。

[2]プロンプトをカスタマイズしてクエリを書き換える

 ここでは、「マンションのルール」に関する質問を、より具体的な管理規約などの検索に結び付けるためにプロンプトをカスタマイズする例を紹介します(リスト1)。

@ShellMethod(key = "translate-rewrite")
    public String translateRewrite(String message){
    // (1) 作り直したプロンプトテンプレート
    var template = """
                ユーザーからの質問を、マンションの管理規約や使用細則のドキュメントにクエリを実行した際により良い結果が得られるように書き換えてください。
                特にユーザーからのクエリはマンション住人の質問と解釈し、相手はマンション管理人もしくはオーナーに対してと解釈してください。
                ただし、質問が抽象的と解釈できる部分は、無理に具体化せず複数の解釈がしやすいように複数のキーワードを含むようにしてください。
                また、無関係な情報については削除し、クエリは簡潔な一文で出力してください。
                元のクエリ:{query}
                書き換えたクエリ:
                """;
    Query query = new Query(message);
    // (2) カスタムプロンプトを適用してインスタンスを作成
    QueryTransformer transformer = RewriteQueryTransformer.builder()
        .chatClientBuilder(builder)
        .promptTemplate(PromptTemplate.builder().template(template).build())
        .build();
    // (3) 処理の実行
    Query transformedQuery = transformer.transform(query);
    return transformedQuery.text();
}
リスト1 RewriteQueryTransformerクラスの利用例(src/main/java/jp/enbind/spring_ai/part7/command/TransferCommand.javaの抜粋)

 (1)では、より質問の目的を明確にしたプロンプトを設定しています。次に、(2)でQueryTransformerのインスタンスを作成、(3)で実際に変換しています。

[3]サンプルを実行する

 サンプルコードを実行すると、以下のような結果が得られます。

shell:>translate-rewrite 駐車場を使いたいのですが
駐車場の利用についてお伺いしたいのですが、どのような手続きが必要でしょうか?

 このように、抽象的で短い質問が、より具体的で検索しやすいクエリに変換されていることが分かります。これにより、VectorStore内で「駐車場」「利用」「手続き」といったキーワードが関連付けられたドキュメントが検索できるようになるはずです。

質問の解釈のバリエーションを作る

 先ほどのQueryTransformerによる質問の書き換えは、クエリをより具体的にすることで検索の精度を高めることを目的としていました。一方、QueryExpanderクラスを利用することで、与えられた質問から想定できる他の質問(バリエーション)を複数作成できます。これによって、質問に対する直接的な回答だけでなく、関連する内容なども含めた回答を得られるようになります。

 現在、Spring AIで用意されているQueryExpanderインタフェースの実装クラスは、以下です。

  • MultiQueryExpander - 与えられた質問を元に関連性がある質問のバリエーションを作成する

 では、MultiQueryExpanderクラスを利用して、実際にプロンプトを拡張してみましょう。

[1]デフォルトのプロンプトを確認する

 MultiQueryExpanderクラスがどのような変換を目的としているのか、まずはデフォルトのプロンプトを見てみましょう。

You are an expert at information retrieval and search optimization.
Your task is to generate {number} different versions of the given query.
Each variant must cover different perspectives or aspects of the topic,
while maintaining the core intent of the original query. The goal is to
expand the search space and improve the chances of finding relevant information.
Do not explain your choices or add any other text.
Provide the query variants separated by newlines.
Original query: {query}
Query variants:
(日本語訳)
あなたは情報検索と検索最適化の専門家です。 あなたのタスクは、与えられたクエリの異なるバージョンを {number} 個生成することです。
各バリエーションは、元のクエリの核心となる意図を維持しつつ、トピックの異なる視点や側面を網羅していなければなりません。目的は、検索空間を拡大し、関連情報を見つける可能性を高めることです。
あなたの選択を説明したり、その他のテキストを追加したりしないでください。 クエリのバリエーションは改行で区切って提供してください。
元のクエリ: {query}
クエリのバリエーション:

[2]プロンプトを作り変えたコードを作る

 先ほどと同じく、プロンプトを書き換えてみましょう。質問者を明確にすることで、マンション特有のキーワード(利用制限、料金、申請手続きなど)を含むバリエーションを生成してみます(リスト2)。

@ShellMethod(key = "expand-multi")
public String expandMultiTemplate(String message){
    // (1) テンプレートを指定
    String templateText = """
        質問者はマンションの住人か、所有者です。その前提で、マンションの理事会、もしくは管理人に質問しています。
        その状況を想定して、情報検索と検索エンジンの最適化の専門家として、与えられた質問の異なるバージョンを {number} 個作成してください。
        多くの場合には、そのものに対する質問だけではなく、利用制限、料金、使い方、または、時間や日程に関する質問などのように解釈を広げるようにしてください。
        // (省略)
        Do not explain your choices or add any other text.
        Provide the query variants separated by newlines.
        質問: {query}
        Query variants:
    """;
    Query query = new Query(message);
    // (2) QueryExpanderインスタンスを作成します
    QueryExpander expander = MultiQueryExpander.builder()
        .promptTemplate(PromptTemplate.builder().template(templateText).build())
        .chatClientBuilder(builder)
        .numberOfQueries(5) // (3) 作成するバリエーション数
        .build();
    // 質問のバリエーションを作成
    List<Query> queryList = expander.expand(query);
    // (省略)
}
リスト2 RewriteQueryTransformerクラスの利用例(src/main/java/jp/enbind/spring_ai/part7/command/TransferCommand.javaの抜粋)

 (1)ではテンプレートをより目的に沿った内容に変更しています。ただし、デフォルトのプロンプトにある出力形式に関する指示はできるだけそのまま残すことが重要です。なぜなら、MultiQueryExpanderは回答をパースしてList<Query>オブジェクトを作成しており、出力フォーマットが変わるとパースに失敗する可能性があるためです。

 そして、(2)のようにテンプレートを指定してQueryExpanderインスタンスを作成します。また、(3)ではバリエーション数を指定しています。もちろん、その場合には、プロンプト側の{{number}}も残しておいてください。

[3]サンプルを実行する

 サンプルコードを実行すると、以下のような結果が得られます。

shell:>expand-multi 駐車場の利用についてお伺いしたいのですが、どのような手続きが必要でしょうか?
駐車場の利用についてお伺いしたいのですが、どのような手続きが必要でしょうか?
駐車場の利用に関する手続きについて教えていただけますか?
マンションの駐車場を利用する際の料金や利用制限はありますか?
駐車場の利用申請はどのような方法で行うのが一般的ですか?
駐車場の空き状況や利用可能時間について教えてください。
駐車場の利用手続きに必要な書類や情報は何ですか?

 特定の暗黙の条件やコンテキストがある場合には、このようにQueryExpanderとカスタムプロンプトを使ってそれらを考慮した複数の質問のバリエーションを作ることは、RAGシステムの品質を向上させる上で重要です。

RetrievalAugmentationAdvisorに組み込む

 これまで見てきたQueryTransformerやQueryExpanderは、RAGパイプラインのフェーズで動作し、クエリの品質を向上させる役割を担っています。これらのカスタマイズされたコンポーネントを実際にRAGシステムに適用するために、前回の記事最後で紹介したRetrievalAugmentationAdvisorに組み込んでみましょう。

[1]RewriteQueryTransformer/MultiQueryExpanderを組み込む

 リスト3は、先ほどのRewriteQueryTransformer/MultiQueryExpanderを使って、RetrievalAugmentationAdvisorクラスに組み込む例です。

@ShellMethod(key = "rag-custom-prompt")
public String ragCustomPrompt(String message){
    // (1) RewriteQueryTransformer用のテンプレート
    var translateTemplate = """
            ユーザーからの質問を、{target} にクエリを実行した際により良い結果が得られるように書き換えてください。
            (省略)
            """;
    // (2) MultiQueryExpander用のテンプレート
    var multiExpandTemplate = """
            質問者はマンションの住人か、所有者です。その前提で、マンションの理事会、もしくは管理人に質問しています。
            (省略)
            """;
    //  RetrievalAugmentationAdvisorのインスタンスを作成
    RetrievalAugmentationAdvisor advisor =
        RetrievalAugmentationAdvisor.builder()
            // (3) 質問を変換する
            .queryTransformers(
                List.of( // (4) 複数指定可能
                    RewriteQueryTransformer.builder()
                        .chatClientBuilder(builder)
                        .promptTemplate(new PromptTemplate(translateTemplate))
                        .build()
                    ))
            // (5) 質問のバリエーションを作る
            .queryExpander(
                MultiQueryExpander.builder()
                   .promptTemplate(new PromptTemplate(multiExpandTemplate))
                   .numberOfQueries(3)
                   .chatClientBuilder(builder)
                   .build())
            //  参考情報を設定するための方法(前回と同様)
            .documentRetriever(
                VectorStoreDocumentRetriever.builder()
                    // (省略)
                ).build();
        // (省略)
}
リスト3 RewriteQueryTransformerクラスの利用例(src/main/java/jp/enbind/spring_ai/part7/command/RagCustomChatCommand.javaの抜粋)

 まず、(1)と(2)でここまでと同様のプロンプトテンプレート用のテキストを用意します。後は、RetrievalAugmentationAdvisorを生成する際に(3)、RewriteQueryTransformer(4)/MultiQueryExpander(5)のインスタンスを設定するだけです。QueryTransformerは複数指定できるので、(4)のようにList形式で渡す点に注意です。

[2]サンプルを実行する

 このコードを実行すると、以下のような結果が得られます。

shell:>rag-custom-prompt 駐車場を使う場合にはどうしたらいいですか
駐車場を使用する場合には、まず駐車場使用契約を結ぶ必要があります。その際、月額の使用料を前月の指定日までに納入しなければなりません。
また、駐車場使用細則に従って利用することが求められます。空き区画がある場合は、抽選や申込順で使用者が選定されることがありますので、詳細については管理組合にお問い合わせいただくことをお勧めいたします。

 このように、複数の事前取得モジュールを組み合わせることで、ユーザーの曖昧な質問に対しても、深い洞察と網羅的な情報に基づいた質の高い回答を提供することが可能になります。

さらにカスタマイズするには

 RetrievalAugmentationAdvisorを構築する際に設定が必須、またはデフォルトで設定されている残りの主要なコンポーネントには、以下のようなものがあります。

DocumentRetriever

 具象クラスとしては、通常はVectorStoreDocumentRetrieverが利用され、VectorStoreでの検索が行われます。しかし、システムが大規模化・複雑化すると、カスタマイズが必要になります。例えば、

  • 用途別に用意した複数のVectorStoreを使い分けたい
  • ベクトル検索だけでなく、全文検索の結果などの情報も合わせて取得したい(=ハイブリッド検索)

のような場合には、DocumentRetrieverコンポーネントのロジックの実装が独自に必要です。

QueryAugmenter

 具象クラスとしては、ContextualQueryAugmenterがデフォルトとして設定されています。ContextualQueryAugmenterクラスは、関連文書が見つからなかった場合に「分かりません」のように答えるような役割を持っていますが、「代わりにWeb検索を行います」といった特定の代替アクションを行いたい場合には、QueryAugmenterコンポーネントのプロンプトやロジックを独自に実装します。

まとめ

 Spring AIのRAGアーキテクチャは、役割ごとにモジュール分解された構造を提供することで、非常に柔軟なシステム構築を可能にしています。しかし、これらの高度な機能を理解し、使いこなすためには、単にAPIの呼び出し方を覚えるというよりも、それぞれのモジュールの目的ごとにプロンプトを設計し、それがどのように機能するかという点に焦点を当てることが、全体像を把握する上で最も重要だといえるでしょう。

 これまでは、RAGというLLM外部にある情報の活用について解説してきましたが、次回からはLLM外部にある機能、つまり、ツール/関数呼び出しやMCP(Model Context Protocol)などを紹介していきます。

筆者紹介

WINGSプロジェクト

有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティー(代表山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手掛ける。2021年10月時点での登録メンバーは55人で、現在も執筆メンバーを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。

サーバーサイド技術の学び舎 - WINGS(https://wings.msn.to/
RSS(https://wings.msn.to/contents/rss.php
X: @WingsPro_info(https://x.com/WingsPro_info
Facebook(https://www.facebook.com/WINGSProject


Copyright © ITmedia, Inc. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

人に頼れない今こそ、本音で語るセキュリティ「モダナイズ」
4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。