Docker版FessのOpenSearchで検索遅延を避ける暫定設定

docker-fessのOpenSearch 3.9.0向けCompose定義に、検索処理の低レベルキャンセルを無効にする設定が追加されました。検索中のキャンセル確認に伴う性能低下を避けるための暫定的な対策です。

docker-fess #91を紹介します。Fessの最新リリースとして確認したのは15.8.0で、15.9のリリースを意味するものではありません。以下は変更を含むCompose定義と対応するOpenSearch環境を利用する場合の説明です。

変更された設定

OpenSearchノードのenvironmentに、次の項目が追加されています。

environment:
  - "search.low_level_cancellation=false"

OpenSearch側の初期値はtrueです。今回のCompose定義ではfalseへ変更します。標準のcompose-opensearch3.yaml、vanilla版、multi-instance版、snapshotの5ノード構成が対象です。変更済み定義を使えば項目は含まれています。独自にComposeファイルを管理している場合は、利用する各OpenSearchノードの設定を確認してください。

環境変数として指定する場合、Compose定義を編集しただけでは稼働中のコンテナへ反映されません。通常のCompose更新手順で対象コンテナを再作成して反映します。反映後はOpenSearchのノード設定APIで、各ノードのsearch.low_level_cancellationがfalseになっていることを確認できます。

どのような遅延への対策か

OpenSearch 3.7〜3.9では、Luceneの検索処理を包むキャンセル確認の実装が、一括処理の高速経路や処理オブジェクトの再利用を妨げることが指摘されています。高頻度語のAND・OR件数集計、前方一致、ワイルドカードなど、一致候補を広く走査する検索が影響を受ける場合があります。

背景と修正内容はOpenSearchのIssue #23107とPR #23117で説明されています。利用者の検索条件やデータ量によって影響は異なるため、この設定による高速化の倍率を一律に保証するものではありません。docker-fessのPRで確認されているのは設定の反映であり、Fess環境での性能測定は行われていません。

キャンセルの応答性との引き換え

falseにすると、Lucene内部を走査している最中のキャンセル確認が行われなくなります。キャンセルやタイムアウトした検索は、次の処理フェーズの境界で停止するため、停止までに時間がかかる場合があります。

検索のキャンセルを全面的になくす設定ではありませんが、重い検索をすぐ止めたい運用ではこの違いを考慮してください。検索時間だけでなく、タイムアウト後の処理継続時間や負荷も検証する必要があります。

今回の対策は、修正を含まないOpenSearch 3.9.0に対する回避策です。Composeのコメントでも、上流の修正を含むリリースがイメージへ取り込まれた後に設定を外すよう記載されています。OpenSearchを更新するときは修正の収録状況を確認し、初期値trueへ戻すか再評価してください。

関連PR

Fessの近接検索で単語の距離を指定する

Fessで"word1 word2"~Nという近接検索の指定を、実際の検索クエリに反映するようにしました。フレーズの間に別の単語が入っていても探せるため、完全一致では取りこぼしてしまう文書を見つける際に役立ちます。

フレーズ検索との違い

通常のフレーズ検索は、解析後の単語が連続する条件で検索します。

"quick fox"

近接検索では、フレーズの後ろに~と数値を付けます。

"quick fox"~1

例えば単純に単語ごとの位置が付く場合、quick brown foxのように間に1語入った文も対象になります。タイトルなどフィールドを指定した検索にも使えます。

title:"quick fox"~2

従来はパーサーが距離の値を保持していても、検索エンジンへ渡すmatch_phraseクエリに反映していませんでした。今回、通常フィールド、指定フィールド、未知のフィールドからのフォールバックに距離を渡し、ハイライトのクエリにも反映します。

距離は文字数ではない

Nが数えるのは、フィールドのアナライザーが生成したトークンの位置です。英語ではおおむね単語間の距離になりますが、除去されたストップワードも位置を占めます。単語の順序を入れ替えた一致にも距離が必要です。

日本語では、一般のタイトル・本文フィールドでCJK bigram、言語別フィールドで形態素解析を使うなど、解析方法によって距離の意味が変わります。単純に「N文字以内」と読み替えることはできません。

"全文 検索"~5
"大阪 おいしい"~10

日本語で複数の語の近さを指定する場合も、語を空白で区切ります。つなげた文字列を指定すると、意図した単語間の近接検索にならない場合があります。少し広めの距離から始め、実際の結果を見ながら絞り込んでください。

どんな検索に向いているか

「運用」と「手順」のように関係する語が同じ箇所に現れる文書を探したいときに使えます。AND検索では離れた場所にある語も一致し、完全なフレーズ検索では間に語が入った文書を落としてしまうので、その中間の条件として利用できます。

近接検索の説明ページもFess 15.9のドキュメントに追加しました。実際のアナライザーによる違いを踏まえて距離を調整するのがポイントです。

関連PR

FessのAIチャットで1つの文書について質問する

FessのAIチャットに、検索結果から選んだ1つの文書を対象に質問する機能を追加しました。複数の検索結果を横断して回答する通常のチャットに加え、「この手順書の要点を教えて」「この資料の条件を説明して」といった使い方ができます。

検索結果から文書チャットを開く

AIチャットが有効な場合、標準のbootstrapテーマでは検索結果に文書について質問するリンクが表示されます。リンクを開くと、対象文書のタイトルを示すバナーが現れ、その文書を参照して会話します。

バナーを解除するか新しいチャットを始めると、通常のチャットへ戻ります。別の文書に切り替えた場合も新しいセッションになります。

文書だけを参照する仕組み

POST /api/v2/chatとPOST /api/v2/chat/streamに、任意のdoc_idを追加しました。文書を指定した場合は検索や検索クエリの再生成を省略し、指定文書を回答の参照元にします。ラベルなどのfieldsやextra_queriesもこの経路では利用しません。

文書の取得には閲覧ロールの確認を適用します。存在しない文書と閲覧権限のない文書はいずれも404となります。チャットの有効・無効、ログイン条件、CSRF対策、ユーザー単位のレート制限も従来と同じです。

サーバーは対象文書の状態を保持しないので、独自クライアントでは会話の各リクエストにdoc_idを送る必要があります。

長い文書は分割して要約する

LLMのコンテキストに収まる文書は1回の呼び出しで回答します。長い文書は分割し、質問に関連する情報を各部分から要約した後、それらをまとめて回答します。

分割数の上限はfess_config.propertiesで指定できます。

rag.chat.document.max.parts=10

上限を超える文書では先頭の部分だけを利用し、ストリーミングAPIはdocument_truncatedの警告を通知します。非ストリーミングAPIには同じ警告を通知する経路がありません。

利用時の注意点

長い文書は質問のたびに要約し直します。標準の上限では部分要約に最大10回、回答生成に1回のLLM呼び出しが必要になるため、待ち時間や利用量を考慮してください。

また、長文の「翻訳」は部分要約から生成するため、全文の逐語的な翻訳にはなりません。対象を1つに絞ることで質問しやすくなりますが、LLMの回答は原文と照らし合わせて確認してください。

関連PR