Erstellen Sie einen korrigierenden RAG-Agenten mit IBM Granite und Tavily

Autor

Jobit Varughese

Technical Content Writer

IBM

Große Sprachmodelle (Large Language Models, LLMs) sind unglaublich leistungsfähig, aber ihr Wissen ist auf ihre Trainingsdatensätze beschränkt. Bei der Beantwortung von Fragen, insbesondere zu spezifischen, sich entwickelnden oder geschützten Informationen, können LLMs halluzinieren oder allgemeine, irrelevante Antworten liefern. Retrieval Augmented Generation (RAG) hilft, indem es dem LLM relevante abgerufene Informationen aus externen Datenquellen zur Verfügung stellt.

Allerdings sind nicht alle RAG-Produkte gleich. Corrective Retrieval Augmented Generation (cRAG) baut nicht einfach auf dem traditionelleren RAG auf, sondern stellt eine deutliche Verbesserung dar. Sie ist so konzipiert, dass sie zuverlässiger ist, indem sie die Qualität und Relevanz der abgerufenen Ergebnisse bewertet. Wenn der Kontext schwach oder irrelevant ist oder aus einer nicht vertrauenswürdigen Quelle stammt, versucht cRAG, durch Korrekturmaßnahmen bessere Informationen zu finden, oder verweigert explizit die Antwort, anstatt eine Antwort zu erfinden. Diese Technik macht cRAG-Systeme zuverlässiger und vertrauenswürdiger für entscheidende Anwendungen wie die Beantwortung richtlinienbezogener Fragen.

Diagramm Relevante Dokumente

In diesem Tutorial erfahren Sie, wie Sie ein robustes korrektives RAG-System (cRAG) aufbauen, indem Sie IBM® Granite®-Modelle auf Watsonx® und LangChain verwenden. Ähnliche Frameworks wie LlamaIndex oder LangGraph können ebenfalls zum Aufbau komplexer RAG-Abläufe mit unterschiedlichen Knoten verwendet werden. Techniken wie Fine-Tuning können die spezifische LLM-Leistung für domänenspezifisches RAG weiter verbessern. LLMs wie die von OpenAI (zum Beispiel GPT-Modelle wie ChatGPT) sind ebenfalls beliebte Optionen für solche Agenten, obwohl sich dieses Tutorial auf IBM Granite konzentriert.

Hier konzentrieren wir uns auf einen Anwendungsfall: die Beantwortung von Fragen zu einem bestimmten Versicherungsvertrag (PDF). Dieses Tutorial führt Sie durch die Implementierung eines komplexen RAG-Algorithmus, der:

  • Informationen abruft aus Ihrem eigenen PDF-Dokument.

  • Wenn die internen Dokumente nicht ausreichen, um die Antwort zu generieren, kann der Agent als Ausweichlösung eine externe Websuche (Tavily) verwenden.

  • Der Agent filtert auf intelligente Weise irrelevante externe Ergebnisse heraus, sodass die Antworten auf private Richtlinien zugeschnitten sind.

  • Der Agent gibt klare, begrenzte Antworten mit Teilinformationen, sofern diese verfügbar sind, oder lehnt Anfragen eindeutig ab, wenn der Kontext fehlt.

Anwendungsfall: Aufbau eines zuverlässigen Abfrageagenten für Versicherungspolicen

Dieses Tutorial demonstriert die Erstellung eines Versicherungsanfrage-Agenten, der Versicherungsdokumente (eine PDF-Broschüre) analysiert und Benutzeranfragen präzise beantwortet. Wir verwenden IBM Granite-Modelle und LangChain, um den Agenten mit robusten Abfrage- und Verifizierungsschritten zu entwickeln, die qualitativ hochwertige, quellengestützte Antworten gewährleisten.

Lassen Sie uns untersuchen, wie die wichtigsten Prinzipien einer zuverlässigen RAG in unserem Anwendungsfall zum Tragen kommen.

Anwendung der Schlüsselprinzipien

Interne Wissensdatenbank (PDF): Die primäre Quelle der Wahrheit für den Makler ist die PDF-Datei Ihrer Versicherungspolice. Es wandelt dieses Dokument in einen durchsuchbaren Vektorspeicher um.

Fallback für externe Suche (Tavily): Sollte die interne Wissensdatenbank nicht über ausreichende Informationen verfügen, kann der Mitarbeiter über Tavily externe Webquellen konsultieren. Tavily ist eine Suchmaschine, die speziell für KI-Agenten und LLMs entwickelt wurde und durch ihre Anwendungsprogrammierschnittstelle (API) für RAG-basierte Anwendungen eine schnellere Abfrage in Echtzeit ermöglicht.

Bewertung des Kontexts: Der LLM-basierte Retrieval-Evaluator (der als Bewerter fungiert) bewertet die Relevanz der aus Ihrer internen PDF-Datei abgerufenen Elemente und stellt gleichzeitig sicher, dass nur qualitativ hochwertige abgerufene Elemente berücksichtigt werden.

Umformulierung von Suchanfragen: Bei der Websuche kann der Agent die Anfrage des Benutzers umformulieren, um die Chancen zu erhöhen, relevante externe Informationen zu finden.

Quellenüberprüfung: Eine LLM-gestützte Prüfung bewertet, ob externe Web-Suchergebnisse für eine private Versicherungspolice relevant sind, indem sie allgemeine Informationen oder Details zu öffentlichen Gesundheitsprogrammen (wie Medi-Cal) herausfiltert. Diese Funktion verhindert die Generierung irreführender Antworten und ermöglicht eine Selbstkorrektur, was bei der Wissensverfeinerung hilft.

Eingeschränkte Generierung: Die abschließende Aufforderung an das LLM weist es ausdrücklich an, nur den bereitgestellten Kontext zu verwenden, präzise Antworten zu geben, anzugeben, wenn Informationen nicht verfügbar sind, oder Teilantworten mit expliziten Einschränkungen zu liefern. Diese Funktion verbessert die Anpassungsfähigkeit und Zuverlässigkeit der generierten Antworten.

Voraussetzungen

Sie benötigen ein IBM Cloud-Konto, um für das Erstellen eines watsonx.ai-Projekts. Stellen Sie sicher, dass Sie sowohl auf Ihren watsonx API-Schlüssel als auch auf Ihre Projekt-ID zugreifen können. Sie benötigen außerdem einen API -Schlüssel für Tavily KI für Websuchfunktionen.

Schritte

Schritt 1. Einrichten Ihrer Umgebung

Sie können zwar aus mehreren Tools wählen, aber dieses Tutorial führt Sie durch die Einrichtung eines IBM Kontos für die Verwendung eines Jupyter Notebook.

  1. Melden Sie sich bei watsonx.ai mit Ihrem IBM Cloud-Konto an.
  2. Erstellen Sie ein watsonx.ai-Projekt. Sie können Ihre Projekt-ID in Ihrem Projekt abrufen. Klicken Sie auf die Registerkarte „Verwalten“. Kopieren Sie dann die Projekt-ID aus dem Abschnitt „Details“ der Seite „Allgemein“. Sie benötigen diese ID für dieses Tutorial.
  3. Erstellen Sie ein Jupyter Notebook.

Dieser Schritt öffnet eine Notebook-Umgebung, in die Sie den Code aus diesem Tutorial kopieren können. Alternativ können Sie dieses Notebook auf Ihr lokales System herunterladen und als Asset in Ihr watsonx.ai-Projekt hochladen. Weitere Granite-Tutorials finden Sie in der IBM Granite-Community. Dieses Tutorial ist auch auf Github verfügbar.

Schritt 2. Einrichtung von watsonx.ai Runtime Service und API-Schlüssel

  1. Erstellen Sie eine Instanz des watsonx.ai Runtime Service (wählen Sie den Lite-Plan als kostenlose Instanz).
  2. Erstellen Sie einen API-Schlüssel (Application Programming Interface) (API).
  3. Verknüpfen Sie den watsonx.ai Runtime Service mit dem Projekt, das Sie in watsonx.ai erstellt haben.

Schritt 3. Installation der Pakete

Um mit dem LangChain-Framework zu arbeiten und IBM WatsonxLLM zu integrieren, müssen wir einige wichtige Bibliotheken installieren. Beginnen wir mit der Installation der erforderlichen Pakete. Dieses Set umfasst langchain für das RAG-Framework, langchain-ibm für die Watsonx-Integration, faiss-cpu für die effiziente Speicherung von Vektoren, PyPDF2 für die Verarbeitung von PDF-Dateien, sentence-transformers für das Abrufen einer Einbettung und requests für Web-API-Aufrufe. Diese Bibliotheken sind für die Anwendung von maschinellem Lernen und NLP-Lösungen von entscheidender Bedeutung.

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

Hinweis: Es ist keine GPU erforderlich, aber die Ausführung kann auf CPU-basierten Systemen langsamer sein. Dieser Schritt öffnet eine Notebook-Umgebung, in die Sie den Code aus diesem Tutorial kopieren können. Dieses Tutorial ist auch auf GitHub verfügbar.

Schritt 4. Importieren erforderlicher Bibliotheken

Importieren Sie als Nächstes alle erforderlichen Module und stellen Sie Ihre API-Schlüssel für watsonx und Tavily zusammen mit Ihrer watsonx Projekt-ID sicher bereit.

# 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 hilft bei der Zusammenarbeit mit dem Betriebssystem.

io ermöglicht die Arbeit mit Datenströmen.

getpass verwendet eine sichere Methode, um sensible Informationen wie API-Schlüssel zu erfassen und zeigt keine Eingaben auf dem Bildschirm an.

PyPDF2.PdfReader ermöglicht die Extraktion von Inhalten aus PDFs.

langchain_ibm.WatsonxLLM ermöglicht es uns, das IBM Watsonx Granite LLM einfach innerhalb des LangChain-Frameworks zu verwenden.

langchain.embeddings.HuggingFaceEmbeddings nimmt ein HuggingFace-Modell und erzeugt die Einbettungen, die für die semantische Suche wichtig sind.

langchain.vectorstores.FAISS ist eine Bibliothek für effiziente Vektor-Speicher und Ähnlichkeitssuche, die es uns ermöglicht, einen Vektorindex zu erstellen und abzufragen.

langchain.text_splitter.RecursiveCharacterTextSplitter hilft dabei, große Textteile in kleinere Stücke zu zerlegen, die für die Verarbeitung von Dokumenten benötigt werden, die sonst nicht in den Speicher passen würden.

langchain.schema.Document stellt eine beliebige Texteinheit mit zugehörigen Metadaten dar und macht es zu einem Baustein in langchain.

requests wird verwendet, um HTTP-Anfragen extern an APIs zu senden.

botocore.client.Config ist eine Konfigurationsklasse, mit der Konfigurationseinstellungen für einen AWS/IBM Cloud Object Storage-Client definiert werden.

ibm_boto3 ist das IBM Cloud Object Storage SDK für Python, das die Interaktion mit Cloud Object Storage unterstützt.

langchain.prompts.PromptTemplate bietet eine Möglichkeit, wiederverwendbare, strukturierte Prompts für Sprachmodelle zu erstellen.

langchain.tools.BaseTool ist die Basisklasse, aus der Sie benutzerdefinierte Tools erstellen, die den LangChain-Agenten zur Verwendung übergeben werden können.

In diesem Schritt werden alle Werkzeuge und Module eingerichtet, die wir benötigen, um Text zu verarbeiten, Einbettungen zu erstellen, sie in einer Vektordatenbank zu speichern und mit watsonx LLM von IBM zu interagieren. Es enthält alle Teile, die für den Aufbau eines realen RAG-Systems erforderlich sind, das eine Reihe von Datentypen beschaffen, abfragen und durchsuchen kann.

Schritt 5. Laden und Verarbeiten einer PDF-Datei aus IBM Cloud Object Storage

In diesem Schritt laden wir die PDF-Versicherungspolice aus IBM Cloud Object Storage. Der Code liest die PDF-Datei, liest den Textinhalt und teilt den Text in kleinere und überschaubare Abschnitte auf. Diese Blöcke werden in numerische Einbettungen umgewandelt und in einem FAISS-Vektorspeicher abgelegt, der uns für die spätere semantische Ähnlichkeitssuche im lokalen Kontext vorbereitet, um die Suchergebnisse zu optimieren.

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 ermöglicht es dem Client, mit IBM Cloud Object Storage zu interagieren.

Bucket ist der Name des Cloud Object Storage-Buckets, der die PDF enthält.

object_key ist der Name der PDF-Datei im Cloud Object Storage-Bucket.

cos_client.get_object(...).read() ruft den Inhalt der PDF-Datei im Cloud Object Storage als Bytes ab.

io.BytesIO konvertiert die PDF-Rohbytes in einen In-Memory-Binärstream in einem Format, das von PdfReader verwendet werden kann.

PdfReader erstellt ein Objekt, das Text aus der PDF-Datei analysieren und extrahieren kann.

page.extract_text() extrahiert den Text einer einzelnen Seite in der PDF-Datei.

RecursiveCharacterTextSplitter ist so konfiguriert, dass der extrahierte Text in Abschnitte von 500 Zeichen mit einer Überlappung von 50 Zeichen aufgeteilt wird, sodass alles im Kontext bleibt.

splitter.split_text(text) führt die Aufteilung aller Seiten des PDF-Textes in kleinere Teile durch.

HuggingFaceEmbeddings lädt ein Satzumwandlungsmodell, das vorab trainiert wurde, um die Textstücke in dichte Vektordarstellungen umzuwandeln.

FAISS.from_texts(chunks, embeddings) erstellt einen In-Memory-FAISS-Index, der es ermöglicht, Textabschnitte anhand ihrer semantischen Ähnlichkeiten zu durchsuchen.

Dieser Schritt behandelt die vollständige Aufnahme eines PDF-Dokuments aus der Cloud in LLM-fähigen Text und eine komfortable Indizierung zum Abrufen in Echtzeit.

Schritt 6. Initialisieren von LLM und Tools

In diesem Schritt konfigurieren Sie das IBM Granite LLM so, dass es die Argumentation Ihres Agenten steuert und es in die Tavily-Websuchfunktion integriert. Die Parameter des LLM sind für sachliche, stabile Antworten eingerichtet.

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 instanziiert den LLM-Wrapper für IBM watsonx und ermöglicht die Interaktion mit Granite-Modellen.

model_id="ibm/granite-3-2b-instruct" ist das IBM Granite-Modell (ein Instruktionsmodell mit 2,7 Milliarden Parametern), das für anweisungsbasierte generative KI-Aufgaben entwickelt wurde.

class TavilySearch(BaseTool) definiert ein benutzerdefiniertes LangChain-Tool zum Ausführen von Websuchen mithilfe der Tavily-API.

tavily_tool = TavilySearch() erstellt eine ausführbare Instanz des benutzerdefinierten Tavily-Suchwerkzeugs.

Wenn wir watsonxLLM initialisieren, werden die url-, apikey- und project_id-Werte aus unseren zuvor eingerichteten Zugangsdaten übergeben, um sich zu authentifizieren und eine Verbindung mit dem Dienst herzustellen. Parameter wie „max_new_tokens“: 300 begrenzen die Antwortlänge und „temperature“: 0,2 kontrolliert die Output-Kreativität und begünstigt deterministischere Ergebnisse.

Die TavilySearch-Klassendefinition enthält eine Beschreibung ihrer Funktion. Ihre Logik ist in der Methode def _run(self, Queries: streng) enthalten. Bei dieser Methode stellen wir eine HTTP-POST-Anfrage an den Tavily-API-Endpunkt und fügen den TAVILY_API_KEY und die Suchanfrage in die JSON-Nutzdaten ein. Anschließend überprüfen wir mit response.raise_for_status(), ob HTTP-Fehler aufgetreten sind,und analysieren die JSON-Antwort, um auf den Inhaltsausschnitt des ersten Suchergebnisses zuzugreifen.

In diesem Schritt wird das Sprachmodell für die Textgenerierung eingerichtet, und es wird ein externes Websuchtool verwendet, um das Wissen über das Sprachmodell zu erweitern.

Schritt 7. Definieren Sie Prompt und Hilfsfunktionen

Dieser Schritt definiert die verschiedenen Prompt-Vorlagen, die das Verhalten des LLM in den verschiedenen Phasen des RAG-Prozesses steuern. Dieser Ansatz umfasst Prompts zur Bewertung der Relevanz interner Dokumentenabschnitte, die Umschreibung von Benutzerabfragen für eine bessere Websuche und einen kritischen neuen Prompt zur Überprüfung der Quelle der Ergebnisse. Es werden auch Hilfsfunktionen für die Bewertung von Chunks und deren Abrufen aus dem Vektorspeicher definiert.

# 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.")

Dieser Schritt definiert die verschiedenen Prompt-Vorlagen, die das Verhalten des LLM in den verschiedenen Phasen des RAG-Prozesses steuern. Eingabeaufforderungen zur Bewertung der Relevanz interner Dokumentenblöcke, Umschreibung von Benutzerabfragen für eine bessere Websuche und ein entscheidendes neues Prompt zur Überprüfung, dass die Quelle der Web-Suchergebnisse enthalten ist. Es werden auch Hilfsfunktionen für die Bewertung von Chunks und deren Abrufen aus dem Vektorspeicher definiert.

PromptTemplate.from_template ist ein Dienstprogramm von LangChain, um eine wiederverwendbare Vorlage für die Erstellung von Prompts zu erstellen.

scoring_prompt_template definiert einen Prompt, der das LLM anweist, als Bewerter zu fungieren und einem bestimmten Kontextabschnitt auf der Grundlage einer Frage einen Relevanzwert (0–5) zuzuweisen.

rewrite_prompt_template definiert einen Prompt, der das LLM dazu anleitet, die ursprüngliche Frage eines Benutzers für die Suche zu verbessern oder klarer zu machen.

CONTEXT_SOURCE_VERIFICATION_PROMPT definiert einen Prompt, der das LLM anweist, zu überprüfen, ob ein Textabschnitt (z. B. aus einer Websuche) aus einem privaten Richtlinienkontext oder einer allgemeinen oder öffentlichen Quelle stammt.

def score_chunks(chunks, query) definiert eine Funktion, die eine Liste von Textabschnitten und eine Abfrage verwendet, die dann das LLM verwendet, um die Relevanz jedes Abschnitts zu bewerten.

def reach_from_vectorstore(query) definiert eine Funktion zum Abrufen der ähnlichsten Dokumente aus dem FAISS Vektorspeicher.

Innerhalb der Funktion score_chunks wird eine leere Trefferliste initialisiert. Für jeden Chunk wird die scoring_prompt_template mit der spezifischen Abfrage und dem Chunk formatiert. Dieser formatierte Prompt wird dann an das LLM gesendet und die Antwort wird entfernt. Die Funktion versucht, die ganzzahlige Punktzahl (eine binäre Punktzahl, wenn sie auf relevant oder nicht relevant vereinfacht wird) zu extrahieren, indem sie die Zeile „Score:“ in der Antwort des Modells identifiziert. Der Chunk wird dann zusammen mit seiner analysierten oder voreingestellten Punktzahl zur bewerteten Liste hinzugefügt. Dieser Teil des Systems fungiert als Retrieval-Evaluator oder Grader.

Die Funktion retrieve_from_vectorstore implementiert eine vectorstore.similarity_search, um die acht relevantesten Dokumentausschnitte basierend auf der Abfrage zu finden, und ruft den page_content aus diesen abgerufenen LangChain-Dokumentobjekten ab.

Dieser Schritt bildet das konzeptionelle Gerüst für das korrigierende RAG-System, sodass sowohl das LLM den Kontext bewertet als auch ermittelt, wie Wissen aus internen und externen Wissensquellen abgerufen werden kann.

Schritt 8. Implementierung der korrigierenden RAG-Logik

Initial retrieval ist die Funktion, die den Vektorspeicher der PDF-Datei durchsucht.

Context scoring nimmt die abgerufenen PDF-Teile und bewertet sie entsprechend ihrer Relevanz.

Fallback to tavily wenn der PDF-Datei nicht genügend relevanter Kontext zur Verfügung steht, wird eine Abfrage an Tavily (Websuche) gesendet.

Source verification ist ein LLM-gestützter Schritt, bei dem überprüft wird, ob die Tavily-Ergebnisse für eine private Richtlinie relevant sind, bevor sie verwendet werden. Diese Funktion verhindert irreführende Antworten aus öffentlichen Gesundheitsprogrammen.

Umformulierung der Suchanfrage und erneute Tavily-Suche: Wenn weiterhin kein passender Kontext gefunden wird, wird die Suchanfrage umformuliert und die Tavily-Suche erneut durchgeführt.

Die endgültige Entscheidung, wenn es einen relevanten Kontext gibt, wird an den LLM mit einer (strengen) Prompt zur Erstellung der Antwort gesendet. Wenn es nach all den brauchbaren Versuchen keinen relevanten Kontext gibt, sendet es eine höfliche Ablehnung.

# 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.")

Im ersten Durchgang des Parameters policy_context_keywords können Sie bestimmte Begriffe aus Ihrer Police (z. B. Name, Versicherer) hinzufügen, um die Suche nach Tavily einzugrenzen.

MIN_CONTEXT_LENGTH definiert die akzeptable Mindestlänge des abgerufenen Kontexts.

SIMIARITY_THRESHOLD definiert die Mindestrelevanzbewertung, die ein Abschnitt haben muss, um als „gut“ zu gelten.

def corrective_rag(...) definiert die Hauptfunktion, die den gesamten RAG-Workflow orchestriert.

Die Funktion corrective_rag beginnt mit der Erstellung von retrieved_context_pieces, um den relevanten Kontext zu erfassen. Zunächst werden chunks_from_vectorstore basierend auf der Abfrage aus dem PDF-Vektorspeicher abgerufen und bewertet. Anschließend bewertet scored_chunks_vector deren Relevanz unter Verwendung des Sprachmodells. Es werden nur good_chunks_vector aufbewahrt, die dem SIMILARITY_THRESHOLD entsprechen. Aus diesen Teilen wird dann der current_context kompiliert.

Wenn der current_context unter MIN_CONTEXT_LENGTH liegt, versucht das System eine Websuche. Es wird eine Tavily_Search-Abfrage erstellt, die möglicherweise Policy_Context_Keywords enthält. Eine direkte Suche (tavily_context_direct) wird durchgeführt. Entscheidend ist, dass ein verification_prompt erstellt und an den LLM gesendet wird, um festzustellen, ob das Websuchergebnis (is_relevant_source) von einer privaten Richtlinie und nicht von einem öffentlichen Programm stammt. Wenn das Resultat YES ist, wird der Kontext hinzugefügt.

Wenn der Kontext weiterhin nicht ausreicht, bereitet sich das System darauf vor, die Abfrage neu zu schreiben. Es verwendet rewrite_prompt, um eine improved_query aus dem LLM abzurufen, und führt dann eine zweite Websuche (tavily_context_rewritten) durch. Auch dieser neue Kontext wird der gleichen Quellenprüfung unterzogen.

Schließlich, wenn len(current_context.strip()) == 0  ist eine letzte Überprüfung. Wenn nach allen Versuchen kein relevanter Kontext gefunden wird, wird eine vordefinierte Ablehnungsmeldung zurückgegeben. Andernfalls wird ein final_prompt mit dem gesamten verifizierten Kontext erstellt und an das Sprachmodell gesendet, um die endgültige Antwort zu generieren.

Die gesamte Funktion corrective_rag behandelt die abgestuften Abruf-, Bewertungs- und Verify-Funktionen von corrective RAG im Detail. Dies ermöglicht eine ständige Aktualisierung der Wissensdatenbank und des Wissensstroms und bietet den Nutzen zuverlässiger und kontextbezogener Antworten.

Schritt 9. Testen Sie das System

Führen Sie abschließend die Funktion correktive_rag mit einer Beispielabfrage aus. Es ist wichtig, dass Sie policy_context_keywords angeben, die sich auf Ihr PDF-Dokument beziehen. Diese Schlüsselwörter tragen dazu bei, dass die Tavily-Websuche für Ihre tatsächliche Politik relevanter wird und verhindern, dass allgemeine oder öffentliche Gesundheitsinformationen Ihren Kontext beeinträchtigen.

Bitte beachten Sie die Druckanweisungen zur Kontextlänge und zu den Verifizierungsergebnissen, um den Informationsfluss zu verstehen.

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 Health", "Care Health Insurance"] definiert eine Liste von Schlüsselwörtern, die für die hochgeladene Versicherungspolice relevant sind und dabei helfen, die Suchergebnisse im Internet einzugrenzen.

query = "..." proxy = "..." definiert die bestimmte Frage, die ein Benutzer stellen könnte.

result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) ruft die Hauptfunktion corrective_rag auf und übergibt die Anfrage des Benutzers sowie richtlinienspezifische Schlüsselwörter, um den gesamten RAG-Prozess zu starten.

print("\n FINAL ANSWER (...)") zeigt vor dem Drucken der generierten Antwort eine eindeutige Kopfzeile an.

Ausgabe(Ergebnisse) gibt die endgültige Antwort aus, die vom corrective_rag-System zurückgegeben wurde.

Dieser Schritt veranschaulicht, wie das vollständige „corrective RAG“-System mit einer Beispielabfrage und Schlüsselwörtern aufgerufen wird, und demonstriert dessen End-to-End-Funktionalität in einem realistischen Szenario.

Die wichtigsten Erkenntnisse

Das „corrective RAG“ implementierte eine vollständig koordinierte interne PDF-Wissensdatenbank mit externem Service (Tavily), um umfassende Informationen für komplexe Anfragen abzurufen.

Der abgerufene Kontext wurde mithilfe von LLM-basiertem Scoring und Überprüfung kritischer Quellen genau ausgewertet und gefiltert, um sicherzustellen, dass gültige und zuverlässige Informationen verwendet werden.

Das System hat seine Fähigkeit unter Beweis gestellt, die externe Suche zu verbessern, indem es Suchanfragen von Benutzern intelligent umformuliert, um gezieltere und qualitativ hochwertigere Informationen anzufordern.

Durch die Verwendung der eingeschränkten Generierung wurde in der Regel eine zuverlässige und kontextgenaue Antwort generiert, und das System lehnte es höflich ab, zu antworten, wenn nicht genügend verifizierte Informationen vorlagen.

Dieses Beispiel veranschaulicht, wie LangChain und IBM Granite LLMs auf watsonx zur Entwicklung leistungsstarker und zuverlässiger KI-basierter Anwendungen in sensiblen Bereichen, wie beispielsweise der Beantwortung von Fragen zu Versicherungspolicen, eingesetzt werden können.

Weiterführende Lösungen
KI-Agenten für Unternehmen

Entwickeln, implementieren und verwalten Sie leistungsstarke KI-Assistenten und -Agenten, die Workflows und Prozesse mit generativer KI automatisieren.

    Entdecken Sie watsonx Orchestrate
    IBM KI-Agenten-Lösungen

    Gestalten Sie die Zukunft Ihres Unternehmens mit KI-Lösungen, denen Sie vertrauen können.

    KI-Agenten-Lösungen erkunden
    IBM Consulting KI-Dienstleistungen

    Die KI-Services von IBM Consulting unterstützen Sie dabei, die Art und Weise, wie Unternehmen mit KI arbeiten, neu zu denken.

    Erkunden Sie die Services im Bereich der künstlichen Intelligenz
    Machen Sie den nächsten Schritt

    Ganz gleich, ob Sie vorgefertigte Apps und Skills anpassen oder mithilfe eines KI-Studios eigene Agentenservices erstellen und bereitstellen möchten, die IBM watsonx-Plattform bietet Ihnen alles, was Sie brauchen.

    Entdecken Sie watsonx Orchestrate watsonx.ai erkunden