全文検索サーバーFessのAI検索モード(RAGチャット)に、検索フィルター機能を追加しました。ラベルやファセットクエリによる絞り込みをチャットUI上から行えるようになります。
背景
FessのAI検索モードでは、ユーザーの質問に対して全文検索で関連文書を取得し、LLMに渡してRAGによる回答を生成しています。しかし、従来のAI検索モードでは、通常の検索画面で利用できるラベルやファセットクエリ(ファイル種別、日付範囲、サイズなど)による検索条件の絞り込みができませんでした。
大量のドキュメントがある環境では、特定のラベルやファイル種別に絞り込んで検索したいケースは多く、AI検索モードでもフィルター機能が求められていました。
主な変更点
チャットUIにフィルターパネルを追加
チャット画面にフィルターの切り替えボタンを追加しました。ボタンをクリックすると折りたたみ式のフィルターパネルが表示され、ラベルやファセットクエリのフィルターボタンが並びます。
- フィルターボタンのクリックで選択/解除を切り替え
- 選択中のフィルター数をバッジで表示
- フィルターを選択した状態で質問すると、絞り込まれた検索結果を元にRAG回答を生成
APIでのフィルターパラメータ対応
REST APIにfields.labelとex_qパラメータを追加し、同期・ストリーミングの両方のチャットAPIでフィルター付き検索をサポートします。
/api/v1/chat: 同期APIでフィルター対応/api/v1/chat/stream: ストリーミングAPIでフィルター対応
フィルターパラメータを指定しない場合は、従来どおりの動作となり後方互換性を維持しています。
セキュリティ対策(ホワイトリスト検証)
フィルターパラメータはユーザーからの入力であるため、クエリインジェクションを防ぐためのホワイトリスト検証を実装しています。
- ラベルフィルター: Fessに設定済みのラベルタイプのみ受け付け、未知のラベル値はデバッグログに記録して除外
- ファセットクエリフィルター: Fessに設定済みのファセットクエリのみ受け付け、同一ファセットグループ内の複数選択はOR結合
実装の詳細
ChatApiManager
parseFieldFilters()とparseExtraQueries()メソッドを追加しました。リクエストからフィルターパラメータを取得し、設定済みのラベルタイプやファセットクエリに対してホワイトリスト検証を行います。
protected Map<String, String[]> parseFieldFilters(final HttpServletRequest request) {
// リクエストロケールとROOTロケールの両方からラベルを取得して許可リストを作成
final Set<String> allowedLabels = new HashSet<>();
ComponentUtil.getLabelTypeHelper()
.getLabelTypeItemList(SearchRequestType.SEARCH, requestLocale)
.stream()
.map(m -> m.get("value"))
.forEach(allowedLabels::add);
// 許可リストに含まれるラベルのみ通過
...
}
ChatClient
chat()、streamChat()、streamChatEnhanced()の各メソッドに、フィルター付きのオーバーロードを追加しました。フィルター条件はsearchDocuments()に渡され、ChatSearchRequestParamsを通じてSearchHelperに伝播します。
チャットUI(chat.jsp / chat.js / chat.css)
フィルターの切り替えボタンと折りたたみ式パネルを追加し、JavaScriptでフィルターの選択状態を管理しています。選択されたフィルターはAPIリクエスト時にパラメータとして送信されます。
まとめ
AI検索モードにフィルター機能を追加することで、ラベルやファセットクエリを使った絞り込み検索がチャットUIから可能になりました。通常の検索画面と同等のフィルタリングをRAGチャットでも利用できるため、大量のドキュメントがある環境での検索精度が向上します。