はじめに:なぜ「供給された記事数」ではRAGの価値が測れないのか
RAG(Retrieval-Augmented Generation)システムを実際に運用すると、多くのチームが「検索から取得された記事数」を品質指標として追跡していることに気づく。取得された記事のIDをカウントし、日次や週次で推移を見る。しかし、この数字はノイズを含む誤計測である可能性が高い。
なぜなら、LLMがプロンプトに供給された情報を無視して、自身の事前学習知識だけで回答を生成した場合でも、カウントは増加してしまうからだ。answerLoopsのようなオープンソースのAIサポートツール(Discord、Slack、GitHub、Telegramなどのチャネルからの問い合わせを一元管理する)でも、既存の機能では記事がコンテキストとして供給された回数をカウントするのみで、それが実際に回答に貢献したかどうかは区別されていなかった可能性がある(Issue #422)。
中小企業やスタートアップの現場では、この「見えないボトルネック」がAI導入の壁となりやすい。ドキュメントの品質改善や検索アルゴリズムのチューニングを正しい方向に進めるために、供給(Exposure)と貢献(Contribution)を分離する設計が不可欠である。本記事では、answerLoopsの開発事例から、貢献度計測の実装パターンと設計判断を解説する。
RAGパイプラインの黒箱:コンテキスト供給と回答生成の断絶
従来のRAG実装の多くは、ベクトルデータベースから類似記事を取得し、そのIDや本文をプロンプトに埋め込むだけで処理を終了する。取得された記事が実際に回答に反映されたかどうかの検証ステップが存在しない。この設計は開発コストを最小限に抑えられる利点があるが、運用段階で「どの記事が本当に効いているか」を把握できなくなる。
answerLoopsの開発履歴を辿ると、この問題が具体的に見える。Issue #422で言及されるcountArticleUsage関数は、回答の内容を見ずに供給されたコンテキストのIDを受け取り、日次カウントを増やす仕組みだった。つまり、LLMがその記事を参照せずに回答を生成した場合でも、カウントは増加する。この「供給=貢献」の仮定が、ドキュメント品質改善や検索チューニングを誤った方向へ導くリスクがある。
特に技術メディアやAPI仕様書など構造化されたドキュメントでは、関連性の低いセクションまで混入しやすい。例えば、認証フローの質問に対してデプロイ手順のセクションが類似度上位に取得され、プロンプトに供給されるケースが想定できる。この場合、カウントは増えるが回答への実際の貢献度はゼロに近い。カウントだけを追うと「この記事はよく使われている」と誤判断し、実際には検索の絞り込み条件を改善すべきという本質的な課題が見えなくなる。
この断絶を埋めるには、回答生成の後に「何が実際に使われたか」を逆算する仕組みが必要となる。単なるログ記録ではなく、回答テキストと供給された記事の内容を突き合わせる評価プロセスをパイプラインに組み込む設計判断が求められる。次のセクションでは、この分離の設計思想を具体的に検討する。
設計の転換点:暴露(Exposure)と貢献度(Contribution)の分離
Issue #422では、生成された回答を供給された知識ベース記事に追跡し、文脈の露出と貢献度を分離する機能の開発が計画された。さらにIssue #437では、供給された記事が生成された回答をどの程度サポートしているかを評価する「Source support」機能の開発が追跡されている。この2つのIssueが示す設計思想の核心は、単一のメトリクスではなく2軸で捉える点にある。
「その記事が提示されたか(Exposure)」と「その記事の内容が回答に含まれたか(Contribution)」を独立した指標として管理する。これにより、検索精度は高いがLLMが参照していないケース、あるいは検索では取得されなかったがLLMの事前知識で補完されたケースを特定できる。前者は記事の要約品質や検索の絞り込み条件の改善に、後者はドキュメントの欠落トピックの特定に直結する。
評価の信頼性を高めるために、モデルラベルだけでなく独立した類似性チェックに合格する必要があり、人間のラベルに対して測定される設計が採用されている可能性がある(Issue #437)。これはLLM自身の自己評価に依存しないことで、主観的な評価バイアスを排除する意図がある。LLMは長い回答を好む傾向や、供給されたテキストをそのまま繰り返す傾向があるため、独立した類似度計算で客観的な基準を設けることが重要である。
| パターン | 計測結果 | 考えられる原因 | 対応策 |
|---|---|---|---|
| Exposure高 / Contribution低 | 記事は供給されたが回答に未反映 | LLMが事前知識で補完、記事の内容が回答に不要 | 記事の要約品質を向上、検索の絞り込み条件を見直す |
| Exposure低 / Contribution高 | 記事は未供給だが回答に反映 | LLMの事前学習知識で補完されている | 該当トピックのドキュメントを新規作成する |
| 両方高い | 記事が供給され回答に反映 | 正常なRAG動作 | 継続的にモニタリング、閾値のドリフトを確認 |
| 両方低い | 記事は供給されたが回答にも未反映 | 検索の関連性が低い、記事と質問が不適合 | 検索アルゴリズムのチューニング、記事の分割粒度を見直す |
この4象限の分類により、検索層と生成層のどちらに問題があるかを切り分けることができる。例えば「Exposure高 / Contribution低」が特定のドキュメントカテゴリに集中している場合、そのカテゴリの記事が長すぎたり、見出しと本文の対応が不十分だったりする可能性が高い。一方「Exposure低 / Contribution高」が頻出するトピックは、ドキュメントの欠落を示唆しており、ナレッジベースの拡張候補として優先的に扱うべきである。
実装パターン:インプロセス評価とデータ保存のトレードオフ
貢献度評価を実装する際、まず直面する設計判断は「いつ・どこで評価を計算するか」である。選択肢には、回答生成の同期フロー内で評価を実行するインプロセス方式、メッセージキューに評価タスクを投げて非同期処理する方式、外部サービスに委譲する方式の3つがある。
answerLoopsの開発では、回答生成後にインプロセスで評価を実行し、永続キューは使用しない設計が採用されている。この判断には明確な理由がある。永続キューを介すると、評価結果の反映に数秒〜数十秒の遅延が発生し、ユーザーが「この回答はどの記事に基づいているか」を確認したいタイミングでデータが未確定になる。また、キューのバックログが蓄積すると、運用上の障害要因が増加する。
インプロセス方式の代償は、LLMモデルの呼び出しオーバーヘッドがそのままレイテンシに加算される点である。回答生成自体に1〜2秒かかるのに対し、類似性チェックに追加で数秒かかる場合、ユーザー体感の応答時間は2倍近くになる。このトレードオフを許容できるかどうかは、用途に依存する。リアルタイムのチャットサポートでは許容しがたいが、バッチ的なドキュメント品質分析であれば非同期に切り替えてもよい。
データ保存の最小化戦略
評価結果の保存において、answerLoopsが採用しているのは「IDと制限付きの抜粋のみを保存する」方針である。回答の全文やコンテキスト記事の全文を保存すると、データ量が一気に膨張し、ストレージコストとプライバシーリスクが同時に高まる。一方、記事IDと回答IDの対応関係、および類似度スコアを伴う短い抜粋(数文程度)だけを残しておけば、後続の集計分析やドリルダウンに十分な情報量が得られる。
この抜粋はキャッシュとしても機能する。同一記事が繰り返し供給される場合、類似度スコアの再計算をスキップして前回の結果を参照できるため、コスト削減に直結する。ただし、記事の内容が更新された場合はキャッシュを無効にするロジックが必要であり、更新検知の仕組み(ハッシュ比較や更新日時フィールドの参照)を併せて設計すべきである。
flowchart TD
A["回答生成完了"] --> B["回答テキストから記事参照箇所を抽出"]
B --> C["各供給記事とコサイン類似度を計算"]
C --> D{"類似度が閾値以上か"}
D -->|Yes| E["貢献度を記録(記事ID・回答ID・スコア・抜粋)"]
D -->|No| F["Exposureのみ記録(貢献度は0)"]
E --> G["メトリクステーブルに反映"]
F --> G
上記のフローは、回答生成が完了した時点からメトリクス更新までの一連の処理を示している。ポイントは、類似度チェックが「各供給記事」に対して個別に実行される点である。供給された記事が5件ある場合、5回の類似度計算が発生する。埋め込みモデルの推論コストが気になる場合は、事前にフィルタリング(例えば回答中に記事のタイトルや見出しが文字列として出現している記事のみを対象にする)を挟むことで計算回数を削減できる。
貢献度判定アルゴリズムの設計:厳格さと言語モデルの限界
貢献度を判定する中核は、回答テキストと供給されたコンテキスト記事のセマンティック類似度を計算する処理である。answerLoopsの設計では、モデルが自分自身の出力に与えたラベル(「この記事に基づいて回答しました」のような自己申告)だけでは不十分とされ、独立した類似性チェックに合格することが証拠として認められる条件になっている。さらに、この自動評価の精度は人間のラベルに対して測定されることで検証される。
埋め込みモデルによる類似度計算
類似度計算には、BGEやCohereなどの埋め込みモデルを用いたコサイン類似度が有効である。単純なキーワードマッチング(例: 回答中に記事の固有名詞が出現しているか)では、言い換えや要約によって意味が保持されていても検出できない。埋め込み空間での距離測定は、表現の揺れに対して頑健である。
ここで設計上の分岐点となるのが閾値の設定である。コサイン類似度の値域は-1から1だが、実用上は0.5〜0.9の範囲で閾値を調整するケースが多い。閾値を高く設定すると、貢献度を過小評価する方向に振れ、「実際に記事の内容を反映しているが、表現が大幅に異なる」ケースが取りこぼされる。逆に低く設定すると、記事と回答が緩く関連しているだけで貢献度と判定され、ノイズが混入する。閾値は固定値ではなく、人間のラベルとの一致率(適合率と再現率)をモニタリングしながら段階的に調整するのが現実的である。
LLM出力形式への対応
もう一つの落とし穴は、LLMの出力形式が統一的でない点である。JSON形式で回答を返すモデル、Markdownの箇条書きで返すモデル、自然文の段落で返すモデルが混在する。記事参照箇所を抽出するロジックは、出力形式によって変える必要がある。JSONであればキー名や構造から参照箇所を特定できるが、自然文の場合は文単位で分割し、各文と記事の類似度を個別に計算する必要がある。
この抽出ロジックを正規表現だけで処理しようとすると、モデルの出力形式が変わるたびに修正が追いつかない。構文解析器(JSONパーサーやMarkdownパーサー)を出力形式ごとに用意し、共通の「文単位テキスト列」に変換する中間層を設けることで、下流の類似度計算ロジックを出力形式から分離できる。この分離により、新しいLLMモデルを追加しても抽出層のみを拡張すれば済む。
技術スタックの選択:MastraとCopilotKitを活用した実献例
RAGパイプラインに貢献度計測を組み込む際、ゼロからエージェント実行基盤とフロントエンド統合を構築するのは、リソースが限られる中小企業・スタートアップにとって現実的ではない。answerLoopsの実装では、バックエンドの推論ロジックとフロントエンドのUI/UXを明確に分離するスタックが採用されている。
Mastra:エージェント実行の基盤
Mastraは、サポートドラフトの作成やウェブサイト上での会話といったエージェントの執行を担うフレームワークである。RAGパイプラインの観点では、ドキュメントの検索・コンテキストの組み立て・回答生成・評価実行という一連のワークフローを、単一のプロセス内で順序制御できる点が重要である。前述したインプロセス評価の設計は、このワークフローが同一プロセス内で完結する前提に成り立っている。Mastraがエージェントのライフサイクルを管理してくれることで、開発者は評価ロジックの「どこに挿入するか」を明示的に指定でき、非同期化やリトライの制御もフレームワークの機能に委ねられる。
複雑なワークフロー(例: 回答生成後に類似度チェックを行い、閾値未満の場合は別の記事を検索して再生成するループ)を構築する場合、Mastraのワークフロー定義機能を活用すると、状態管理と分岐制御をコードレベルで記述できる。これにより、評価結果に応じた動的なパイプライン再実行が可能になる。
CopilotKit:フロントエンド統合の基盤
CopilotKitは、カスタムウィジェットインターフェースの基盤を提供する。貢献度計測の文脈では、ユーザーが回答を確認する際に「この回答はどの記事に基づいていますか」という情報をUI上に表示する機能が重要になる。CopilotKitのウィジェット機構を活用することで、回答テキストの隣に供給記事のリストと貢献度スコアを並べて表示するコンポーネントを、バックエンドの実装と独立して開発できる。
両者を組み合わせる設計利点は、保守性の向上にある。バックエンドで評価アルゴリズムの閾値を調整したり、埋め込みモデルを差し替えたりする場合、フロントエンドの表示ロジックには影響しない。逆に、UIのレイアウトを変更する場合も、バックエンドの推論ロジックを触る必要がない。この分離は、チーム内での役割分担(バックエンド担当とフロントエンド担当が並行して作業できる)にも直結し、開発速度の確保に寄与する。
ただし、スタックの選定時に注意すべき点もある。MastraとCopilotKitのバージョン互換性、および自社が使用するLLMプロバイダとの統合方法については、各フレームワークの公式ドキュメントで最新の対応状況を確認する必要がある。特に、自社でホスティングするLLMモデル(On-premise環境)と組み合わせる場合、推論エンドポイントの接続方法がクラウドホスティングのケースと異なる場合があるため、事前の検証を推奨する。
データモデル設計:追跡情報の格納とクエリ最適化
貢献度データを長期保存する際の設計判断として、まず「時系列ログ形式」と「記事IDと回答IDのマッピングテーブル」のどちらを採用するかを明確にする必要がある。時系列ログはデバッグには便利だが、記事単位の集計クエリ(例:「直近30日で貢献率が最も高い記事は何か」)を実行するたびに全ログをスキャンする必要が生じ、データ量が増えるほど応答時間が劣化する。一方、マッピングテーブル形式では、記事IDと回答IDの結合に対してインデックスを張ることで、この種の集計クエリを定数時間近傍で処理できる。
データ項目の設計では、answerLoopsの実装方針に倣い、記事IDと「制限付きの抜粋」のみを保存する戦略が有効である。これは、回答全文やコンテキスト全文を格納せずに、後続の分析に必要な最小限の情報だけを保持する設計判断である。制限付き抜粋は、再計算時のコスト削減という意味でも機能する。例えば、評価アルゴリズムの閾値を調整して過去データに再適用する場合、全文を再取得するのではなく、保存済みの抜粋に対して類似度計算を再実行できるため、LLM呼び出しコストを大幅に抑えられる。
インデックス設計では、運用時に頻繁に発生するクエリパターンを想定して設計する。具体的には、(1)貢献率上位N記事の抽出、(2)一定期間内に一度も貢献していない記事(未使用記事)の特定、(3)特定の記事に対する貢献度の時系列推移の取得、の3つを基本とする。これらに対して複合インデックスを適用し、ファイルスキャンを避けることが重要である。
データウェアハウスとの連携を視野に入れる場合、BIツールでの可視化にはSQL互換のクエリエンジンが適している。RDBMSに直接BIツールを接続する構成も可能だが、本番環境へのクエリ負荷を分離したい場合は、CDC(Change Data Capture)経由で分析用DBに複製する構成を検討すべきである。
stateDiagram-v2
[*] --> A
A: 回答生成
A --> B
B: インプロセス評価
B --> C
C: 類似性チェック
C --> D
D: メトリクス保存
D --> E
E: 集計・可視化
E --> [*]
この状態遷移のうち、D(メトリクス保存)の段階で保存されるデータのスHEMAを事前に設計しておくことが、後のクエリ最適化の前提となる。特に、貢献度スコアのデータ型(浮動小数点の桁数)や、タイムスタンプの精度(秒単位かミリ秒単位か)は、集計クエリの結果に影響するため、設計段階で決定しておくべきである。
運用における落とし穴:バイアスとコストの管理
貢献度評価を自動化した場合でも、評価結果の妥当性を担保するためには人間によるサンプリング検証が不可欠である。answerLoopsの設計方針では、証拠として認められるにはモデルラベルに加え独立した類似性チェックに合格する必要があり、最終的には人間のラベルに対して測定される。これは、自動化された評価が「一見合理的だが実際には外れている」ケースを検出するための最終防衛線として機能する。実務上、週次で数十件程度のサンプルを人間が確認する運用を定例化することで、評価アルゴリズムのドリフトを早期に発見できる。
LLMの評価モデル自体にもバイアスが存在する。例えば、長い回答に対して高スコアを付ける傾向(長さバイアス)や、特定の文体・フォーマットを好む傾向が報告されている。このバイアスを放置すると、「長い回答を生成するプロンプト設計」が貢献度スコアの向上に見えるという誤った最適化が起きる。対策としては、貢献度スコアの他に、回答の正確性(ファクトチェック)や簡潔性(冗長性の低さ)といった補完指標を併用し、複数指標のバランスで判断する設計が望ましい。
コスト管理の観点では、インプロセス評価はリクエストごとにLLM呼び出しが発生するため、トラフィックが集中する時間帯には推論コストが線形に増加する。バッチ処理(一定間隔で蓄積した回答を一括評価)や非同期評価(回答を即座に返し、評価はバックグラウンドで実行)との併用により、リアルタイム性とコストのバランスを調整できる。ただし、非同期評価に切り替えた場合、ユーザーが回答を確認した時点では貢献度データがまだ存在しないため、UI側の表示ロジック(例:「評価中」のステータス表示)との整合性を確保する必要がある。
ドキュメントの更新頻度が高い環境では、貢献度データのstale化に注意する必要がある。記事が大幅に改訂された場合、過去の貢献度データは改訂前の記事に対する評価であり、現在の状態を反映しない。記事のハッシュ値やバージョン番号をメトリクスレコードに併記し、記事更新時に過去のデータを「非有効」とマークするクリーンアップ戦略を設計しておくことで、分析結果の信頼性を維持できる。
answerLoopsの事例から学ぶ:オープンソースツール設計の教訓
answerLoopsは、Discord、Slack、GitHub、Telegramなどのチャネルからの問い合わせを一元管理するオープンソースのAIサポートツールである。この設計の意図は、問い合わせが単一のチャネルに集中しない中小企業・スタートアップの実際の運用形態に適合させることにある。チャネルごとに個別のRAGパイプラインを構築すると、貢献度データの集計がチャネル単位に断片化し、ナレッジベース全体の品質評価が困難になる。一元管理により、どのチャネルから来た問い合わせであっても同じ貢献度データモデルにマッピングできる。
ライセンスとデプロイモデルの面では、answerLoopsはAGPL-3.0ライセンスで提供され、Docker Composeスタックとして自己ホスティング可能であり、独自のLLMモデルを使用できる。この組み合わせは、データプライバシーが重視される現場にとって重要な設計判断である。社内の技術ドキュメントやAPI仕様書といった機密性の高いナレッジを、外部クラウドサービスに送信せずに貢献度評価を行うことが可能になる。特に、顧客との契約でデータ送出を禁じられている場合、この自己ホスティング構成は実用上の必須要件となる。
自社ナレッジベースに同様の追跡機能を組み込む際の設計教訓として、以下の3点が挙げられる。第一に、チャネル抽象化レイヤーを先に設計し、貢献度データモデルをチャネル非依存にすること。第二に、LLMモデルの差し替え可能性を前提に、評価ロジックをモデル固有のプロンプトと分離してカプセル化すること。第三に、Docker Composeのような宣言的なデプロイ構成により、開発・ステージング・本番環境での挙動差を最小限に抑えること。これらは、answerLoopsのアーキテクチャから読み取れる、実運用を前提とした設計思想である。
まとめ:RAGの質を高めるための次のステップ
RAGシステムの改善サイクルを回すには、「どの記事が回答に実際に貢献したか」を定量的に把握する仕組みが不可欠である。単純なコンテキスト供給回数カウントは、LLMが情報を無視した場合までカウントしてしまうため、検索アルゴリズムのチューニングやドキュメント品質改善を誤った方向へ導くリスクがある。露出(Exposure)と貢献度(Contribution)を分離した2軸の分析に移行することで、「検索は命中しているがLLMが参照していない」記事や「検索では拾われていないが参照された場合がある」記事といった、単一指標では見えないボトルネックを特定できる。
実装においては、以下の設計判断が鍵となる。評価は回答生成後にインプロセスで実行し、永続キューへの依存を避けることでリアルタイム性を確保する。データ保存ではIDと制限付き抜粋のみを保持し、データ量と分析有用性のバランスを取る。類似性チェックの閾値は、人間のラベルに対する精度と再現性を基準に段階的に調整し、LLM評価モデルのバイアスを補完指標で相殺する。これらの判断は、answerLoopsのようなオープンソースツールが実際に直面した設計課題から導かれたものであり、自社システムに適用する際の判断材料として活用できる。
貢献度計測は一度導入して終わりではなく、ドキュメントの改訂やLLMモデルの更新に伴って継続的に校正する必要がある。評価アルゴリズムの閾値、埋め込みモデルの選択、データ保存の粒度といったパラメータは、ナレッジベースの規模や更新頻度に応じて最適値が異なる。定期的なサンプリング検証と指標の相関分析を通じて、これらのパラメータを運用データに基づいて調整していくことが、RAGシステムの質を持続的に高めるための現実的なアプローチである。
関連記事
- AIエージェントの暴走を防ぐ:while(true)ループの限界とEventStoreによる状態管理の実践
- AIエージェント開発の「脱フレームワーク化」:LangChain依存からの脱却と実装の本質
- RAG(検索拡張生成)の仕組みを徹底解説!LLMの精度を向上させる実装パターン
参考
本記事は海外の技術トレンド「Building an Open-Source Support Tool That Turns Your Docs into Reusable Knowledge」(DEV)で話題のテーマをもとに、両儀システムソリューションズが独自に解説したものです。