IBM GraniteとTavilyを使用した修正的なRAGエージェントの構築

著者

Jobit Varughese

Technical Content Writer

IBM

大規模言語モデル (LLM) は、非常に強力ですが、その知識はトレーニング データセットに限定されています。特に特定の、変化する、独自の情報に関する質問に答える際、LLMはハルシネーションを見せたり、一般的で無関係な回答を提供したりすることがあります。検索拡張生成(RAG) は、外部データソースから取得した関連する情報を LLM に提供することで役立ちます。

ただし、すべての RAG が同じように作成されるわけではありません。修正検索拡張生成(cRAG)は、単に従来のRAGの上に構築されているだけでなく、大幅な改善を表しています。取得した成果の品質と関連性を評価することで、より堅牢になるように考案されています。コンテキストが脆弱、無関係、または信頼できない情報源からの場合、cRAGは回答をでっち上げているのではなく、修正措置を通じてより適切な情報を見つけようとするか、明示的に回答を拒否します。この技術により、ポリシー関連の質問に答えるなどのクリティカルなアプリケーションにおいて、cRAGシステムの信頼性と信用性が高まります。

図の関連資料

このチュートリアルでは、IBM GraniteモデルをWatsonxとLangChainで使用して、堅牢な修正RAG(cRAG)システムを構築する方法について説明します。LlamaIndexLangGraphなどの同様のフレームワークも、個別のノードを持つ複雑な RAG フローの構築に使用できます。微調整などの手法により、ドメイン固有の RAG の特定の LLM の性能をさらに向上させることができます。OpenAIのようなLLM(たとえば、ChatGPTのようなGPTモデル)もこのようなエージェントの一般的な選択肢ですが、このチュートリアルではIBM Graniteに焦点を当てています。

ここでは、特定の保険契約書(PDF)に関する質問に答えるというユースケースに焦点を当てます。このチュートリアルでは、次のような高度なRAGアルゴリズムの実装について説明します。

  • 独自の PDF ドキュメントから情報を取得します

  • 内部の文書が回答を生成するのに不十分な場合、エージェントはフォールバックとして外部Web検索(Tavily)を使用できます。

  • エージェントは無関係な外部の成果をインテリジェントに除外し、回答がプライベートポリシーに合わせて調整されるようにします。

  • エージェントは、利用可能な場合は部分的な情報を使用して明確かつ限定的な応答を行い、コンテキストが欠落している場合は明確に拒否します。

ユースケース:信頼できる保険契約照会エージェントの構築

このチュートリアルでは、保険契約文書(PDFのパンフレット)を分析し、ユーザーの質問に正確に答えるように設計された保険契約クエリー・エージェントの作成について説明します。IBM GraniteモデルとLangChainを使用して、高品質でソースに制約のある回答を保証する堅固な検索および検証ステップを備えたエージェントを構築します。

信頼できるRAGの基本原則がユースケースにどのように適用されるかを理解しましょう。

主要原則の適用

内部知識ベース(PDF):エージェントの主要な情報源は、お客様の保険証書PDFです。このドキュメントを検索可能なベクトル・ストアに変換します。

外部検索フォールバック (Tavily):内部ナレッジベースに十分な情報がない場合、エージェントはTavilyを通して外部のウェブソースを参照することができます。Tavilyは、AIエージェントとLLM専用に構築された検索エンジンで、RAGベースのアプリケーションーション向けのアプリケーション・プログラミング・インターフェース(API)を通じて、より高速なリアルタイムの検索を実現します。

コンテキストスコア:LLMベースの検索評価者(採点者の役割)は、高品質の検索項目のみが含まれるようにしながら、内部PDFから検索された項目の関連性にスコアを提供します。

クエリの書き換え:ウェブ検索の場合、エージェントはユーザーのクエリを言い換えて、関連する外部情報を見つける可能性を高めることができます。

ソースの検証:LLMを利用したチェックでは、外部のウェブ検索成果が民間保険契約に関連しているかどうかを評価し、ヘルスプログラム(Medi-Calなど)に関する一般情報や詳細を除外します。この機能により、誤解を招く回答の生成を防ぎ、自己修正を可能にするため、知識の洗練を支援します。

制約された生成:LLMへの最終プロンプトでは、提供されたコンテキストのみを使用するか、正確な回答を提供する、情報が利用できない場合の状態、または明示的な制限を用いて部分的な回答を提供するように厳密に指示します。この機能により、生成された応答の適応性と信頼性が向上します。

前提条件

watsonx.aiプロジェクトを作成するにはIBM Cloudのアカウントが必要です。watsonx APIキーとプロジェクトIDの両方にアクセスできることを確認してください。Web検索機能を使用するには、Tavily AIのAPIキーも必要になります。

手順

ステップ1. 環境を設定する

いくつかあるツールの中から選択することもできますが、このチュートリアルでは、Jupyter Notebookを使用してIBMアカウントを設定する方法について説明します。

  1. IBM Cloudアカウントを使用して、watsonx.aiにログインします。
  2. watsonx.aiプロジェクトを作成します。 プロジェクトIDはプロジェクト内から取得できます。「管理」タブをクリックし、「全般」ページの「詳細」セクションからプロジェクトIDをコピーします。このチュートリアルではこのIDが必要になります。
  3. Jupyter Notebookを作成します。

このステップでは、このチュートリアルのコードをコピーできるノートブック環境を開きます。あるいは、このノートブックをローカル・システムにダウンロードし、watsonx.aiプロジェクトにアセットとしてアップロードすることもできます。さらにGraniteのチュートリアルを表示するには、IBM® Graniteコミュニティをご覧ください。このチュートリアルはGithubでも公開されています。

ステップ2. watsonx.aiランタイム・サービスとAPIキーを設定する

  1. watsonx.aiランタイム・サービス・インスタンスを作成します(無料インスタンスであるLiteプランを選択します)。
  2. アプリケーション・プログラミング・インターフェース(API)キーを生成します。
  3. watsonx.aiで作成したプロジェクトにwatsonx.aiランタイム・サービスを関連付けます。

ステップ3. パッケージのインストール

LangChainのフレームワークを使用し、IBM® WatsonxLLMを統合するには、いくつかの必須ライブラリーをインストールする必要があります。必要なパッケージをインストールすることから始めましょう。このセットには、RAGフレームワーク用のlangchain、watsonx統合用のlangchain-ibm 、効率的なベクターストレージ用のfaiss-cpu 、PDFを処理するためのPyPDF2埋め込みとWeb API呼び出しの要求を取得するためのsentence-transformersが含まれています。これらのライブラリは、機械学習およびNLPソリューションを適用する上でクリティカルです。

# Install Libraries
!pip install langchain langchain-ibm faiss-cpu PyPDF2 sentence-transformers requests

注:GPUは不要ですが、CPUベースのシステムでは実行速度が低下する可能性があります。このステップでは、このチュートリアルのコードをコピーできるノートブック環境を開きます。このチュートリアルは、GitHubでも公開されています。

ステップ4. 必要なライブラリーをインポートする

次に、必要なすべてのモジュールをインポートし、watsonxとTavilyのAPIキーとwatsonxのプロジェクトIDを安全に指定します。

# Import required libraries

import os
import io
import getpass
from PyPDF2 import PdfReader
from langchain_ibm import WatsonxLLM
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
import requests
from botocore.client import Config
import ibm_boto3
from langchain.prompts import PromptTemplate
from langchain.tools import BaseTool

# Watsonx
WML_URL = "https://us-south.ml.cloud.ibm.com"
WML_API_KEY = getpass.getpass(" Enter Watsonx API Key: ")
PROJECT_ID = input(" Enter Watsonx Project ID: ")

# Tavily
TAVILY_API_KEY = getpass.getpass(" Enter Tavily API Key: ")

print(" Credentials loaded.")

osは、オペレーティングシステムとの連携に役立ちます。

ioは、データのストリームを扱うことができます。

getpassは、APIキーなどの機密情報を安全な方法でキャプチャし、画面にインプットを表示しません。

PyPDF2.PdfReaderは、PDFからのコンテンツ抽出を可能にします。

langchain_ibm.WatsonxLLMを使用すると、LangChainフレームワーク内でIBM Watsonx Granite LLMを簡単に使用できるようになります。

langchain.embeddings.HuggingFaceEmbeddings は、HuggingFaceモデルを使用し、セマンティック検索にとって重要な埋め込みを生成します。

langchain.vectorstores.FAISS は、効率的なベクトルストレージと類似性検索のためのライブラリで、ベクトルインデックスを構築してクエリを実行できます。

langchain.text_splitter.RecursiveCharacterTextSplitterは、大きなテキストを、メモリに収まらないようなドキュメントを処理するために必要な小さな塊に分割するのに役立ちます。

langchain.schema.Document、は任意のテキスト単位と関連するメタデータを表し、langchainの構成要素となります。

requestsは、APIに対して外部からHTTPリクエストを行う際に使用されます。

boxocore.client.Configは、AWS/IBM Cloud Object Storageクライアントの構成設定を定義するために使用される構成クラスです。

ibm_boto3は、Cloud Object Storageとの対話に役立つIBM Cloud Object Storage SDK for Pythonです。

langchain.prompts.PromptTemplate は、言語モデル用の再利用可能な構造化プロンプトを作成する方法を提供します。

langchain.tools.BaseToolは、LangChainエージェントに使用させるカスタムツールを構築するための基本クラスです。

このステップでは、テキストの処理、埋め込みの作成、ベクトル・データベースへの保管、IBMのWatsonxLLMとの対話に必要なすべてのツールとモジュールをセットアップします。さまざまなデータ型のソーシング、クエリ、検索が可能な実際のRAGシステムの作成に必要なすべての部分を確立します。

ステップ5. IBM® Cloud Object StorageからPDFをロードして処理する

このステップでは、IBM Cloud Object Storageから保険契約のPDFをロードします。コードはPDFを読み取り、テキスト内容を読み取り、テキストをより小さく管理しやすいチャンクに分割します。これらのチャンクは数値埋め込みに変換され、FAISSベクトル・ストアに保存され、後でローカル・コンテキストで意味論的類似性検索を行い、成果を最適化できます。

import os, types
import pandas as pd
from botocore.client import Config
import ibm_boto3

def __iter__(self): return 0

cos_client = ibm_boto3.client(service_name='s3',
ibm_api_key_id='YOUR_IBM_API_KEY',
ibm_auth_endpoint="https://iam.cloud.ibm.com/identity/token",
config=Config(signature_version='oauth'),
endpoint_url='https://s3.direct.us-south.cloud-object-storage.appdomain.cloud')

bucket = 'YOUR_BUCKET_NAME'
object_key = 'YOUR_OBJECT_KEY'

streaming_body_2 = cos_client.get_object(Bucket=bucket, Key=object_key)['Body']
pdf_bytes = io.BytesIO(streaming_body_2.read())

reader = PdfReader(pdf_bytes)
text = ""
for page in reader.pages:
extracted = page.extract_text()
if extracted:
text += extracted

print(f" Extracted {len(text)} characters from PDF.")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(text)
print(f" Split into {len(chunks)} chunks.")

embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
vectorstore = FAISS.from_texts(chunks, embeddings)

print(f" Created FAISS index.")

ibm_boto3.clientでは、クライアントがIBM Cloud Object Storageと対話できるようになります。

バケットは、PDFを含むCloud Object Storageの名前です。

object_keyは、Cloud Object Storageバケット内のPDFの名前です。

cos_client.get_object(...).read()は、Cloud Object StorageにあるPDFファイルの内容をバイトとして取得します。

io.BytesIOは、PDFの生バイトを、PdfReaderで使用できる形式のメモリ内バイナリストリームに変換します。

PdfReaderは、PDFからテキストを解析し抽出することができるオブジェクトを作成します。

page.extract_text()は、PDF内の1ページのテキストを抽出します。

RecursiveCharacterTextSplitterは、抽出されたテキストを50文字の重なりを持つ500文字のチャンクに分割するように設定されています。

splitter.split_text(text)は、PDF テキストのすべてのページを小さなチャンクに分割します

HuggingFaceEmbeddingsは、テキストチャンクを密なベクトル表現に変換するために事前に学習された文変換モデルをロードします。

FAISS.from_texts(チャンク、埋め込み)は、テキストのチャンクを意味上の類似性によって検索できるようにするメモリ内FAISSインデックスを構築します。

このステップでは、クラウドからLLM対応テキストへのPDFドキュメントの完全な取り込みと、リアルタイム検索のための快適なインデックス作成が処理されます。

ステップ6. LLMとツールを初期化する

このステップでは、エージェントの推論を促進し、Tavily Web検索機能と統合するようにIBM Granite LLMを構成します。LLMのパラメーターは、事実に基づいた安定した応答を実現するために設定されています。

llm = WatsonxLLM(
model_id="ibm/granite-3-2b-instruct",
url=WML_URL,
apikey=WML_API_KEY,
project_id=PROJECT_ID,
params={
"max_new_tokens": 300, # ~2-3 paragraphs, good for corrective RAG
"temperature": 0.2, # low temperature = more factual, stable answers
}
)

print(" Watsonx Granite LLM ready.")
class TavilySearch(BaseTool):
name: str = "tavily_search"
description: str = "Search the web using Tavily for extra info."

def _run(self, query: str):
response = requests.post(
"https://api.tavily.com/search",
json={"api_key": TAVILY_API_KEY, "query": query}
)
response.raise_for_status()
return response.json()['results'][0]['content']


tavily_tool = TavilySearch()

WatsonxLLMは、IBM watsonxのLLMラッパーをインスタンス化し、Graniteモデルとのやりとりを可能にします。

model_id="ibm/granite-3-2b-instruct"は、命令ベースの生成AIタスク用に設計されたIBM Graniteモデル(27億パラメーターの命令モデル)です。

class TavilySearch(BaseTool)は、Tavily APIを使ってウェブ検索を実行するためのカスタム LangChainツールを定義します。

tavily_tool = TavilySearch()は、カスタムTavily検索ツールの実行可能インスタンスを作成します。

watsonxLLMを初期化すると、以前に設定した認証情報のURL、APIkey、Project_idの値が認証され、サービスに渡されます。「max_new_tokens」:300などのパラメータは、応答の長さを制限し、「温度」:0.2は出力の創造性を制御し、より決定的な結果を優先します。

TavilySearchクラスの定義には、その機能の説明が含まれています。そのロジックはdef _run(self, query: str)メソッドに含まれています。このメソッドでは、TAVILY_API_KEYとJSONペイロードの検索クエリを含むTavily APIエンドポイントに、HTTP POSTリクエストを実行します。次に、Response. Raise_for_status()を使用してHTTPエラーがあるかどうかを確認します。JSON応答を解析し、最初の検索結果のコンテンツ・スニペットにアクセスすることもできます。

このステップでは、テキスト生成用の言語モデルをセットアップし、言語モデルの知識を強化する方法として外部のWeb検索ツールを含めます。

ステップ7. プロンプト・テンプレートとヘルパー関数を定義する

このステップでは、RAGプロセスのさまざまな段階でLLMの動作をガイドするさまざまなプロンプト・テンプレートを定義します。このアプローチには、内部文書のチャンクの関連性をスコアリングするプロンプト、Web検索を改善するユーザー・クエリーの書き換え、Web検索結果の出所をVerifyするためのクリティカルな新しいプロンプトが含まれます。チャンクをスコアリングし、ベクトル・ストアから取得するためのヘルパー関数も定義されています。

# Define Prompt Templates and Helper Functions

# Prompt for scoring the relevance of retrieved chunks
scoring_prompt_template = PromptTemplate.from_template(
"""
You are an evaluator. Score the relevance of the context chunk to the given insurance question.

Question: "{query}"

Context:
\"\"\"
{chunk}
\"\"\"

Respond only in this format:
Score: <0-5>
Reason: <one line reason>
"""
)

# Prompt for rewriting the user's query for better web search results
rewrite_prompt_template = PromptTemplate.from_template(
"""
You are a helpful assistant. Improve the following question to be clearer for an insurance information search.
Focus on making the query more specific if possible.

Original Question: "{query}"

Rewrite it to be clearer:
"""
)

# NEW: Prompt for verifying if Tavily context is from a relevant source (private policy vs. public program)
CONTEXT_SOURCE_VERIFICATION_PROMPT = PromptTemplate.from_template(
"""
You are an expert at identifying if a piece of text is from a general, public, or unrelated source
versus a specific, private, or relevant policy document.

Read the following context and determine if it appears to discuss general information,
public health programs (like Medi-Cal, Medicaid, Medicare, NHS, government-funded programs, state-funded),
or information that is clearly *not* specific to a private insurance policy like the one
the user might be asking about (assuming the user is asking about their own private policy).

If the context explicitly mentions or heavily implies public health programs, or is too general
to be useful for a specific private policy question, respond with "NO".
Otherwise (if it seems like it *could* be from a private policy context, a general insurance guide,
or does not explicitly mention public programs), respond with "YES".

Context:
\"\"\"
Response:
"""
)


# Function to score chunks using the LLM
def score_chunks(chunks, query):
scored = []
for chunk in chunks:
prompt = scoring_prompt_template.format(query=query, chunk=chunk)
response = llm(prompt).strip()

try:
# Extract score using more robust parsing
score_line = [line for line in response.splitlines() if "Score:" in line]
if score_line:
score = int(score_line[0].replace("Score:", "").strip())
else:
score = 0 # Default to 0 if score line not found
except Exception as e:
print(f" Could not parse score for chunk: {e}. Response: {response[:50]}...")
score = 0 # Default to 0 on error

scored.append((chunk, score))
return scored

# Function to retrieve documents from FAISS vector store
def retrieve_from_vectorstore(query):
# Retrieve top 8 similar documents from your PDF content
docs = vectorstore.similarity_search(query, k=8)
return [doc.page_content for doc in docs]

print(" Prompt templates and helper functions defined.")

このステップでは、RAGプロセスのさまざまな段階でLLMの動作をガイドするさまざまなプロンプト・テンプレートを定義します。内部文書のチャンクの関連性をスコアリングするためのプロンプト、より適切なWeb検索のためにユーザー・クエリを書き換える、およびWeb検索結果のVerifyが含まれていることを確認するためのクリティカルな新しいプロンプト。チャンクをスコアリングし、ベクトル・ストアから取得するためのヘルパー関数も定義されています。

PromptTemplate.from_templateは、プロンプトを作成するための再利用可能なテンプレートを作成するためのLangChainのユーティリティ関数です。

logging_prompt_templateは、LLMが評価者として機能し、質問に基づいて特定のコンテキストチャンクに関連性スコア(0~5)を割り当てるように指示するプロンプトを定義します。

rewrite_prompt_templateは、LLMがユーザーの元の質問を改善したり、検索しやすくするためにガイドするプロンプトを定義します。

CONTEXT_SOURCE_VERIFICATION_PROMPTは、LLMに指示するプロンプトを定義し、テキスト(例えば、Web検索から)がプライベート・ポリシー・コンテキスト、または一般または公開ソースからのものであるかを確認します。

def score_chunks(chunks, query)は、テキストチャンクのリストとクエリを受け取り、LLMを使って各チャンクの関連性をスコア化する関数を定義すします。

defget_from_vectorstore(query)は、FAISS ベクトル・ストアから最も類似したドキュメントを取得する関数を定義します。

score_chunks関数内では、空のスコア付きリストが初期化されます。各チャンクに対して、scoring_prompt_templateは、特定のクエリーとチャンクでフォーマットされます。このフォーマットされたプロンプトがLLMに送信され、応答が除去されます。この関数は、モデルの応答内の「Score:」行を識別することにより、整数スコア(関連性または関連性のないものに単純化される場合は2進数スコア)を抽出しようとします。チャンクは、解析またはデフォルトのスコアとともにスコア付けリストに追加されます。システムのこの部分は、検索評価者または採点者として機能します。

関数retrieve_from_vectorstoreは、vectorstore.similarity_searchで、クエリに基づいて最も関連性の高い8つのドキュメントチャンクを検索し、検索されたLangChain Documentオブジェクトからpage_contentを取得します。

このステップでは、LLMがコンテキストと内部および外部の知識ソースから知識を取得する方法を評価できるように、修正RAGシステムの概念足場が構築されます。

ステップ8. 修正RAGロジックを実行する

最初の取得は、PDFのベクトルストアをスキャンする機能です。

コンテキストスコアリングは、関連性に応じてコンテキストスコアを付けるために、取得されたPDFチャンクを取ります。

PDFから十分な関連コンテキストがない場合、Tavilyにフォールバックし、Tavily(ウェブ検索)に問い合わせます。

ソース検証は、Tavilyの成果がプライベート・ポリシーに関連しているかどうかを使用する前に確認するLLMを利用したステップです。この機能は、公衆ヘルスプログラムからの誤解を招く回答を防ぎます。

クエリの書き換えと2回目のタビリーサーチは、まだ良いコンテキストがなければ、クエリを書き換えて再度タビリーサーチを試みます。

最終決定は、関連するコンテキストがある場合、回答を作成するための(厳密な)プロンプトを備えてLLMに送信されます。すべての実行可能な試行の後に関連するコンテキストがない場合は、丁寧な拒否を送信します。

# Implement the Corrective RAG Logic

MIN_CONTEXT_LENGTH = 100 # Adjust this based on how much minimal context you expect for a partial answer
SIMILARITY_THRESHOLD = 3 # Only scores >= 3 used for vector store chunks

def corrective_rag(query: str, policy_context_keywords: list = None):
"""
Executes the Corrective RAG process to answer insurance queries.

Args:
query (str): The user's question.
policy_context_keywords (list, optional): Keywords related to the specific policy
(e.g., ["Super Star Health", "Care Health Insurance"]).
Used to make external searches more specific. Defaults to None.
Returns:
str: The final answer generated by the LLM or a predefined refusal.
"""
retrieved_context_pieces = [] # To store all relevant pieces found throughout the process

# Initial vector search & Scoring (from your PDF)
chunks_from_vectorstore = retrieve_from_vectorstore(query)
scored_chunks_vector = score_chunks(chunks_from_vectorstore, query)
good_chunks_vector = [chunk for chunk, score in scored_chunks_vector if score >= SIMILARITY_THRESHOLD]
retrieved_context_pieces.extend(good_chunks_vector)

current_context = "\n\n".join(retrieved_context_pieces)
print(f" Context length after initial vector scoring: {len(current_context)}")

# Prepare specific query for Tavily by optionally adding policy keywords
tavily_search_query = query
if policy_context_keywords:
tavily_search_query = f"{query} {' '.join(policy_context_keywords)}"

# Fallback: Tavily direct search (only if current context is too short from vector store)
if len(current_context) < MIN_CONTEXT_LENGTH:
print(f" Context too short from internal docs, trying Tavily direct with query: '{tavily_search_query}'...")
tavily_context_direct = tavily_tool._run(tavily_search_query)

if tavily_context_direct:
# --- NEW STEP: Verify Tavily Context Source ---
# Ask the LLM if the Tavily result seems to be from a private policy context or a public program
verification_prompt = CONTEXT_SOURCE_VERIFICATION_PROMPT.format(context=tavily_context_direct)
is_relevant_source = llm(verification_prompt).strip().upper()

if is_relevant_source == "YES":
retrieved_context_pieces.append(tavily_context_direct)
current_context = "\n\n".join(retrieved_context_pieces) # Re-combine all good context
print(f" Context length after Tavily direct (verified and added): {len(current_context)}")
else:
print(f" Tavily direct context source rejected (e.g., public program): {tavily_context_direct[:100]}...")
# Context is NOT added, so it remains short and triggers the next fallback or final refusal

# Fallback: Rewrite query + Tavily (only if context is still too short after direct Tavily)
if len(current_context) < MIN_CONTEXT_LENGTH:
print(" Context still too short, rewriting query and trying Tavily...")
rewrite_prompt = rewrite_prompt_template.format(query=query)
improved_query = llm(rewrite_prompt).strip()

# Add policy keywords to the rewritten query too
if policy_context_keywords:
improved_query = f"{improved_query} {' '.join(policy_context_keywords)}"

print(f" Rewritten query: '{improved_query}'")
tavily_context_rewritten = tavily_tool._run(improved_query)

if tavily_context_rewritten:
# --- NEW STEP: Verify Rewritten Tavily Context Source ---
verification_prompt = CONTEXT_SOURCE_VERIFICATION_PROMPT.format(context=tavily_context_rewritten)
is_relevant_source = llm(verification_prompt).strip().upper()

if is_relevant_source == "YES":
retrieved_context_pieces.append(tavily_context_rewritten)
current_context = "\n\n".join(retrieved_context_pieces) # Re-combine all good context
print(f" Context length after rewritten Tavily (verified and added): {len(current_context)}")
else:
print(f" Tavily rewritten context source rejected (e.g., public program): {tavily_context_rewritten[:100]}...")

# --- Final Decision Point ---
# Now, `current_context` holds ALL the "good" and "verified" context we managed to gather.
# The decision to call the LLM for an answer or give a hard refusal is based on `current_context`'s length.

# Final check for absolutely no good context
# This triggers only if *no* relevant internal or external context was found or verified.
if len(current_context.strip()) == 0:
print(" No good context found after all attempts. Returning absolute fallback.")
return (
"Based on the information provided, there is no clear mention of this specific detail "
"in the policy documents available."
)

# If we have *any* context (even if short), pass it to the LLM to process
# The LLM will then decide how to phrase the answer based on its prompt instructions
# (exact, partial, or full refusal if context is irrelevant or insufficient based on its own reasoning).
final_prompt = (
f"You are a careful insurance expert.\n"
f"Use ONLY the following context to answer the user's question. If the context is too short "
f"or does not contain the answer, you must indicate that.\n"
f"Context:\n```\n{current_context}\n```\n\n" # Pass the gathered context
f"User's Question: {query}\n\n" # Pass the original query for the LLM's reference
f"NEVER add new details that are not in the context word-for-word.\n"
f"If the context clearly says the answer, give it exactly as written in the context, but in prose.\n"
f"If the context does not mention the topic at all, or the answer is not in the context, say:\n"
f"\"I'm sorry, but this information is not available in the provided policy details.\"\n"
f"If the context partially mentions the topic but does not directly answer the specific question (e.g., mentions 'dental' but not 'wisdom tooth removal'), reply like this:\n"
f"\"Based on the information provided, here’s what is known: [quote relevant details from the context related to the broad topic.] "
f"There is no clear mention of the specific detail asked about.\"\n"
f"Do NOT assume. Do NOT make up extra information.\n"
f"Do NOT generate extra questions or conversational filler.\n"
f"Final Answer:"
)

return llm(final_prompt)

print(" Corrective RAG logic implemented.")

policy_context_keywordsのパラメータの最初のパスでは、Tavilyの検索を絞り込むために、保険契約から特定の用語(例えば、その名前、保険会社)を追加することができます。

MIN_CONTEXT_LENGTHは、検索されるコンテキストの最小許容長を定義します。

SIMILARITY_THRESHOLDは、チャンクが「良い」とみなされるための最小関連性スコアを定義します。

def corrective_rag(...)は、修正RAGワークフロー全体を調整するメイン関数を定義します。

corrective_rag関数は、関連するコンテキストを収集するためのretrieved_context_piecesを作成することから始まります。まず、クエリに基づいてPDFベクトル・ストアからchunks_from_vectorstoreを取得してスコア付けし、次にstorage_chunks_vectorが言語モデルを使用してその関連性を評価します。SIMILARITY_THRESHOLDを満たすgood_chunks_vectorのみが保持されます。current_contextは、これらの断片からコンパイルされます。

current_contextMIN_CONTEXT_LENGTHを下回っている場合、システムはWeb検索を試みます。tavily_search_queryを構築し、policy_context_keywordsを組み込む可能性があります。直接検索(tavily_context_direct)が実行されます。重要なのは、Verify_promptが作成され、LLMに送信され、Web検索結果(is_relevant_source)がパブリック・プログラムではなくプライベート・ポリシーからのものであるかどうかを判断することです。そうである場合、コンテキストが追加されます。

コンテキストが不十分な場合、システムはクエリを書き直す準備をします。rewrite_promptを使用してLLMからimproved_queryを取得し、2回目のWeb検索(tavily_context_rewritten)を実行します。この新しいコンテキストも、同じソース検証を受けます。

len(current_context.strip()) == 0最後のチェックです。すべての試行後に関連するコンテキストが見つからない場合は、事前定義された拒否メッセージが返されます。それ以外の場合は、検証済みのすべてのコンテキストを使用してfinal_promptを作成し、言語モデルに送信して最終的な回答を生成します。

全体のCollective_rag関数は、修正RAGの段階的な取得、スコアリング、検証機能を詳細に処理します。これにより、知識ベースと知識ストリームを常に更新できるようになり、堅牢で状況に応じた回答が得られるというメリットがもたらされます。

ステップ9. システムをテストする

最後に、サンプルのクエリーでcorrective_rag関数を実行します。PDF文書に固有のpolicy_context_keywordsを提供することが重要です。これらのキーワードは、TavilyのWeb検索が実際のヘルス政策により関連するものになり、一般的なヘルスプログラムや公衆ヘルスプログラムの情報がコンテキストを汚染するのを防ぐのに役立ちます。

印刷ステートメントを観察してコンテキストの長さと成果を観察し、情報の流れを理解します。

query = "How does the policy cover for In-Patient Hospitalisation?"
result = corrective_rag(query)

print("\n FINAL ANSWER:\n")
print(result)

policy_specific_keywords = ["Super Star ヘルス", "Care ヘルス Insurance"]は、アップロードされた保険契約に関連するキーワードのリストを定義し、Web検索結果を絞り込むのに役立ちます。

query ="..."は、ユーザーが質問する可能性のある特定の質問を定義します。

result = corrective_rag(query, policy_context_keywords=policy_specific_keywords)は、メインのcorrective_rag関数を呼び出し、ユーザーのクエリとポリシー固有のキーワードを渡して、RAGプロセス全体を開始します。

print("\nFINAL ANSWER (...)")は、生成された答えを印刷する前に、クリアヘッダを表示します。

print(result)は、corrective_ragシステムから返された成果をアウトプットします。

このステップでは、サンプルのクエリとキーワードを使用して完全な修正RAGシステムを呼び出す方法を示し、実際のシナリオでのエンドツーエンドの機能を実証します。

重要ポイント

RAGによる修正版では、社内のPDFナレッジ・ベースと外部サービス(Tavily)を完全に連携させて、複雑なリクエストに対する包括的な情報を取得できるようにしました。

LLMベースのスコアリングとクリティカルなソース検証を使用して、取得されたコンテキストを通じて正確に評価およびフィルタリングを行い、有効で信頼性の高い情報が使用されていることを確認します。

このシステムは、ユーザーのクエリーをインテリジェントに書き換えて、よりターゲットを絞った高品質の情報を要求することで外部検索を改善できることを実証しました。

制約生成を使用することで、信頼性が高く文脈に沿った正確な回答が通常生成され、検証済みの既知の情報が十分にない場合、システムは丁寧に回答を拒否しました。

この事例では、watsonx上のLangChainとIBM Granite LLMを使用して、保険保険に関する質問など機密性の高い領域で強力で信頼できるAIベースのアプリケーションを開発する方法を示しました。

関連ソリューション
ビジネス向けAIエージェント

生成AIを使用してワークフローとプロセスを自動化する強力なAIアシスタントとエージェントを構築、デプロイ、管理しましょう。

    watsonx Orchestrateの詳細はこちら
    IBM AIエージェント・ソリューション

    信頼できるAIソリューションでビジネスの未来を構築します。

    AIエージェント・ソリューションの詳細はこちら
    IBM®コンサルティング AIサービス

    IBMコンサルティングAIサービスは、企業がAIをトランスフォーメーションに活用する方法を再考するのに役立ちます。

    人工知能サービスの詳細はこちら
    次のステップ

    事前構築済みのアプリケーションとスキルをカスタマイズする場合でも、AIスタジオを使用してカスタム・エージェント・サービスを構築し、デプロイする場合でも、IBM watsonxプラットフォームが対応します。

    watsonx Orchestrateの詳細はこちら watsonx.aiの詳細はこちら