Fessのゼロヒット検索を調べて関連クエリーを検索ログから生成する

Fessの検索ログに、ゼロヒットの原因を詳しく調べる機能と、利用者の検索語の言い換えから関連クエリーを生成する機能が追加されました。以前紹介した分析ダッシュボードに加え、検索改善を実施するための操作が増えています。

ゼロヒットのログへ移動する

概要・クエリータブに表示するゼロヒットの検索語から、該当するログ一覧を開けます。一覧のヒット数フィルターで「すべて」「ゼロヒットのみ」「1件以上」を切り替え、同じ条件でCSVを出力できます。ゼロヒットの表には最終検索日時も表示します。

利用者の分析にはロール・グループごとの検索数、ユーザー数、ゼロヒット率が加わりました。1回の検索をそのユーザーの複数のロール・グループへ計上するため、各行の合計は全体の検索数と一致しない場合があります。個人用の検索ロールは表から除外します。

検索ログから関連クエリーを生成する

管理画面の関連クエリーで「検索ログから生成」を実行します。同じセッションで短時間に行った検索の組み合わせを、検索語の言い換えとして集計します。例えば最初の検索語の後に、より具体的な語で検索してヒットした場合が候補になります。

主な初期値は次のとおりです。fess_config.propertiesで調整できます。

related_query.generate.days=30
related_query.generate.term.size=100
related_query.generate.query.size=5
related_query.generate.min.sessions=3
related_query.generate.session.interval=10

過去30日、仮想ホストごとに最大100語、1語につき最大5件の関連語が対象です。元の語と関連語の両方に3以上の異なるセッションが必要で、検索し直す間隔は10分以内です。検索ログとユーザー情報の記録が有効であることが前提になります。既存の登録語は変更せず、新規の語だけを作成します。

生成結果を確認して運用する

関連クエリーは公開され、検索語のOR展開にも利用します。生成時にはsuggest.search.log.permissionsに従ってゲスト公開できるログだけを対象とし、フィールド指定やワイルドカードなどの検索構文、NGワード、ヒットしない後続検索を除外します。作成後は通常の関連クエリーとして編集・削除できます。

全ログを無制限に解析する機能ではありません。1語あたりのログ取得は初期値で1000件、セッション200件、後続ログ2000件に制限し、語の長さは2〜50文字です。登録件数は関連クエリーキャッシュの上限にも従います。処理は同期実行で同時実行を拒否するため、大量のログがある場合は期間と件数を絞って実行してください。

ゼロヒット率が高いからといって、関連語の追加だけで解決するとは限りません。クロール対象や文書の閲覧権限、必要なコンテンツが存在するかも、ログを手がかりに確認するとよさそうです。

利用できるバージョン

Fess 15.9.0に含まれる予定の機能になります。現時点では、まだ、リリースしていないので、今後のテストで変更される可能性もあります。

関連PR

Fessのベクトル検索でチャンク生成の失敗と保留を見分ける

Fessのベクトル検索では、クロールした本文をチャンクへ分割し、埋め込みモデルでベクトルを作成します。ジョブが終了しても、すべての文書が検索に使える状態になったとは限りません。開発版では、チャンク生成結果の集計と埋め込みサービスの一時停止からの復旧を改善しました。

ジョブの成功件数を確認する

Content Chunk Vector Indexerの結果はfess-chunk.logのChunk vector processing result:で確認できます。従来は、文書へfailやskippedを書き込めた場合もSucceededに数えていました。修正後はベクトル生成に成功した件数だけを成功とし、失敗、スキップ、保留を分けて表示します。

Processed 11 documents. Succeeded: 7, Failed/Skipped: 4. Failed: 1, skipped: 3, left pending: 0.

これはPRで確認された集計例です。Failed/Skippedは成功していない件数の合計で、保留も含みます。left pendingは未処理の文書全体ではなく、この実行で処理を試みて完了状態を保存できずに残した件数です。

content_chunk_statusがdoneなら完了、failなら原因調査が必要な失敗、skippedなら対象本文やチャンク生成条件による除外です。空本文、生成チャンクなし、チャンク数上限超過、存在しないチャンク処理名などでもskippedになります。ジョブ結果だけでなく文書の状態分布も併せて確認します。

一時的なモデル停止と文書固有の失敗を分ける

OpenSearchの再起動直後は、モデル情報がDEPLOYEDでも実際のノードではまだモデルを読み込んでいない場合があります。従来、この間に処理した文書がfailとなり、通常の再実行では選ばれなくなることがありました。

修正後のOpenSearch埋め込みクライアントは、応答がない接続などを再試行し尽くした場合や、推論リクエストが拒否され、モデルのプロファイルでどのノードにもデプロイされていないと確認できた場合に、文書を保留へ残します。モデルが利用可能になった後、次のジョブ実行で再処理します。同じ実行中に自動で完了する保証ではありません。

モデルのプロファイルAPIには推論と同じ認証情報を使います。403などで読めない場合は、モデル未ロードと判断できず、従来どおり失敗になる場合があります。他の埋め込みプラグインへOpenSearch固有の判定が自動で広がるわけでもありません。

モデルが正常に動作しているのに本文を拒否した場合、応答を解析できない場合、ベクトルの件数や次元が一致しない場合は、引き続きfailです。まず原因を修正してから、次のシステムプロパティを一時的に設定してジョブを実行します。

content_chunker.job.retry_failed=true

初期値はfalseです。復旧のための1回の実行後は元へ戻します。skippedはこの設定では再選択されないので、原因を修正したうえで対象文書の状態を解除して再処理する必要があります。

モデルとインデックスの次元をそろえる

content_chunker.embedding.dimensionを未設定でインデックスを作成すると、マッピングは警告なしに768次元になります。一方、実行時の次元設定は未設定を同じようには補いません。モデルの次元を作成前から明示しておく必要があります。

次元を変えるには対応するインデックスの再作成が必要です。同じ次元の別モデルに変更した場合も、以前の文書ベクトルと新しい検索ベクトルは異なる空間になるため、次元チェックに通ることだけで互換性を判断せず、全文書のベクトルを作り直します。チャンクサイズは文字数ですが、モデルの入力上限はトークン数で、Fessがモデル上限へ自動切り詰めするわけではありません。

検索側の待ち時間と制限も確認する

埋め込みHTTP接続の上限は、CPU数から計算する標準の順位統合スレッド数に可用性チェック用の1接続を加え、最低5接続とするようになりました。16コアでは26接続です。少数の停止したリクエストが接続を占有する状況を緩和しますが、実行スレッドがすべて応答待ちになる問題は残ります。rank.fusion.threadsを独自に変更しても、この接続上限は追従しません。

Fess側での順位統合に使うrank.fusion.timeoutの初期値は10000ミリ秒です。初期値falseのrank.fusion.engine.enabledをtrueにして検索エンジン側で統合する場合、このタイムアウトは適用されません。OpenSearch埋め込みの応答タイムアウトは初期値60000ミリ秒、retry.maxは初回を含めて3回なので、応答待ちに再試行間の待機時間も加わる点に注意します。

検索エンジン側の統合が拒否されてFess側へ切り替わる場合、同じリクエスト内で検索語が同じなら作成済みの検索ベクトルを再利用する修正も入りました。別リクエスト間で共有するキャッシュではなく、再試行で検索語が変われば再計算します。

また、faissとcosinesimilを使う場合のcontent_chunker.search.min_scoreの換算を修正しました。たとえばコサイン類似度の下限を0.35と設定すると、OpenSearchへ送る下限スコアは0.675です。0.35は調整例で、innerproductやl2ではこのコサイン類似度の下限を適用せず警告します。

関連PR

Fessの設定変更を再起動せずに反映する範囲を確認する

Fessの設定を管理画面で保存したり、system.propertiesを編集したりしても、起動時に読み込んだ値が残る項目がありました。開発版ではこの動作を改善し、テーマやRAGチャットの利用条件、埋め込み接続の設定変更を稼働中の処理へ反映するようにしました。

保存した設定と実際の動作をそろえる

system.propertiesはファイル変更時に再読み込みされます。今回の修正では、再読み込み後も残っていた解析済みの値を更新します。対象はrag.chat.permissions、rag.chat.labels、sort.value、label.value、virtual.host.valueです。手動編集とバックアップからの設定復元も対象になります。

rag.chat.permissionsとrag.chat.labelsの初期値は空です。前者はチャット利用権限の追加制限なし、後者は検索対象ラベルの追加制限なしという意味で、文書そのものの検索権限を解除する設定ではありません。たとえば、すでに有効化したチャットを特定グループへ限定する設定は次のようになります。

rag.chat.permissions={group}developer

対応版では、保存・再読み込み後のアクセス判定で新しい値を使います。グループ内外のアカウントで実際に確認しておくと、反映漏れを見つけやすくなります。

theme.defaultはテーマ解決時に読み直します。また、osdd.link.enabled=autoの場合、SSO種別の変更に合わせてブラウザー向け検索登録リンクと/osddの提供可否を判定し直します。

埋め込み接続の変更を反映する

OpenSearch埋め込みのcontent_chunker.embedding.opensearch.api.url、username、passwordはリクエストごとに参照します。HTTPクライアントに古い認証情報を保持していたために、接続先を変更しても以前の認証情報を送る問題を改善しています。

埋め込みプロバイダーのtimeoutとconnect.timeoutを変更すると、次の利用時にHTTPクライアントを作り直します。OpenSearchの応答タイムアウトの初期値は60000ミリ秒です。処理中のリクエストがすべて直ちに新しいタイムアウトへ切り替わるわけではなく、旧クライアントは進行中の処理のために一定時間残ります。

content_chunker.embedding.opensearch.timeout=30000
content_chunker.embedding.opensearch.availability.check.interval=60

上のタイムアウト30秒は調整例です。可用性チェックの初期値は60秒で、間隔変更は実行待ちのチェックが終わって次のチェックを予約するときに反映します。短い間隔へ変更してから戻しても、古い周期のチェックが重複して残らないようにする修正も入りました。

マルチモーダルプラグインでは、CLIP接続先と画像設定も利用時に読み直します。画像の初期値は224×224、受け付ける最大サイズは3000×2000、形式はpng、接続先はhttp://localhost:51000です。この変更には対応するプラグインも必要です。

再起動が必要な設定を区別する

fess_config.propertiesの設定は引き続き再起動が必要です。system.propertiesの値が優先され、-Dfess.system.*はその値がない場合のフォールバックになる順序も変わりません。起動コマンドの-Dを書き換える操作が、稼働中のJVMを直接変更するわけではありません。

管理画面の「システムプロパティ」に対応するapp.valueは、手動編集による変更も5秒ごとに確認します。この欄で設定した値を変更・削除すると更新・復元しますが、fess.*以外のキーが起動オプションなど別の経路ですでに設定されている場合は、それを優先します。

SPNEGOの設定反映も別PRで提案されていますが、確認時点ではFessプラグインのPR #2は未マージです。ライブラリ側の変更だけで利用できるとは案内できません。提案されている実装でも、keytabの差し替え、同じ場所のkrb5.confやlogin.confの内容変更、AD側だけのサービスアカウントのパスワード変更は再起動が必要とされています。

関連PR