- AI
- RAG
- エンジニアリング
カスタマーサポートのための RAG:AI の返信を本当に正確にする方法
素の LLM は、もっともらしい答えを作り上げてしまいます。検索拡張生成(RAG)は、AI の回答を自社のコンテンツにもとづかせます。AskAIs でのやり方を、順を追って紹介します。
AskAIs チーム
7 分で読めます
素の LLM に「パスワードをリセットするには?」と聞くと、もっともらしいけれどおそらく間違った答えが返ってきます。ボタンも、手順も、URL も違います。RAG(検索拡張生成)は、モデルが答える前に、自社のコンテンツから適切な箇所をプロンプトに入れることで、この問題を解決します。
パイプラインの全体像
取り込み、検索、生成の 3 段階です。
1. 取り込み
- PDF、DOCX、TXT、Markdown、CSV、JSON のファイルをアップロードするか、Web サイトをクロールするか、Training で Q&A のペアを追加します。
- ワーカーがテキストを抽出し、約 2,000 文字(およそ 500 トークン)のチャンクに分割します。チャンク同士の重なりは 200 文字です。Q&A のペアは、1 組で 1 つのチャンクのままです。
- 各チャンクは
text-embedding-3-small(1,536 次元)で埋め込みに変換されます。 - チャンクは
pgvector拡張を入れた PostgreSQL に保存され、コサイン距離用の HNSW インデックスが作成されます。
2. 検索
訪問者のメッセージに事実が必要なときは、直近のメッセージを埋め込みに変換し、ワークスペースのチャンクに対してコサイン距離で検索します。あいさつや雑談では、このステップを省きます。クエリを簡略化すると次のとおりです:
-- Simplified. The real query also checks that the document is
-- ready, enabled, in date and visible on the visitor's platform.
SELECT c.id, c.content,
1 - (c.embedding <=> $1::vector) AS similarity
FROM knowledge_chunks c
WHERE c.tenant_id = $2
ORDER BY c.embedding <=> $1::vector
LIMIT $3; -- 3 × top-K candidates, re-ranked afterwards必要な数の 3 倍の候補を取得して並べ替え、類似度のしきい値(約 0.3)を下回るものを除外し、1 つのドキュメントからは最大 2 件に絞ったうえで、上位 5 件(デフォルト)をモデルに渡します。
3. 生成
チャンクは区切られた参照ブロックに入れて、システムプロンプトの末尾に追加されます。モデルには、このブロックを指示ではなくデータとして扱うこと、質問に答える参照だけを使うこと、そこにない製品情報をでっち上げないことを指示しています。
訪問者が製品に関する事実を尋ね、関連する情報が見つからない場合は、モデルをまったく呼び出しません。訪問者にはその情報がないことを伝え、チャットは担当者に渡されます(担当者への自動引き継ぎをオフにしている場合を除く)。
コストを抑える
大きなナレッジベースでもコストを抑えられるのは、次の 3 つのおかげです:
- 埋め込みキャッシュ:同じ検索テキストには同じ埋め込みが使われます。キーはモデル名とテキストの SHA-1 ハッシュで、Redis に 24 時間保存されます。
- プロンプトキャッシュ:Claude モデルでは、リクエストで Anthropic にプロンプトのキャッシュを依頼します。OpenAI は長いプロンプトを自動でキャッシュします。参照ブロックは返信のたびに変わるため、主に効果があるのはプロンプトの固定部分です。
- しきい値によるスキップ:しきい値を超えるチャンクがなければ、プロンプトには何も追加しません。
RAG だけでは足りないとき
RAG は「〜するには?」という質問が得意です。しかし「注文した商品はどこ?」には答えられません。そのデータはドキュメントにないからです。
そのために、AskAIs には Custom tools があります。注文、予約、在庫など、自社システムの HTTP エンドポイントを記述しておくと、AI は返信を書く途中でそれを呼び出せます。一般的な質問はナレッジから、個別の質問は自社システムから答え、それ以外はチームに回します。