Cree un agente de RAG correctiva mediante IBM Granite y Tavily

Autor

Jobit Varughese

Technical Content Writer

IBM

Los modelos de lenguaje de gran tamaño (LLM) son increíblemente potentes, pero su conocimiento se limita a sus conjuntos de datos de entrenamiento. Al responder preguntas, especialmente sobre información específica, en evolución o patentada, los LLM pueden alucinar o proporcionar respuestas generales e irrelevantes. Generación aumentada por recuperación (RAG, por sus siglas en inglés) ayuda al proporcionar al LLM información recuperada relevante de fuentes de datos externas.

Sin embargo, no todos los RAG son iguales. La generación aumentada por recuperación correctiva (cRAG) no se basa simplemente en el RAG más tradicional, sino que representa una mejora significativa. Está diseñado para ser más robusto al evaluar la calidad y relevancia de los resultados recuperados. Si el contexto es débil, irrelevante o proviene de una fuente poco confiable, cRAG intenta encontrar mejor información a través de acciones correctivas o se niega explícitamente a responder en lugar de fabricar una respuesta. Esta técnica hace que los sistemas cRAG sean más confiables y dignos de confianza para aplicaciones críticas, como responder preguntas relacionadas con políticas.

Diagrama de documentos relevantes

En este tutorial, aprenderá a crear un sistema de RAG correctiva (cRAG) robusta mediante el uso de modelos IBM Granite en watsonx y LangChain. También se pueden utilizar infraestructuras similares, como LlamaIndex o LangGraph, para crear flujos de RAG complejos con nodos distintos. Técnicas como el ajuste pueden mejorar aún más el rendimiento de LLM específico para RAG específica de dominio. Los LLM como los de OpenAI (por ejemplo, modelos GPT como ChatGPT) también son opciones populares para dichos agentes, aunque este tutorial se centra en IBM Granite.

Aquí, nos centraremos en un caso de uso: responder preguntas sobre un documento específico de una póliza de seguro (un PDF). Este tutorial lo guiará en la implementación de un sofisticado algoritmo RAG que:

  • Recupera información de su propio documento PDF.

  • Si los documentos internos no son suficientes para generar la respuesta, el agente puede utilizar una búsqueda web externa (Tavily) como alternativa.

  • El agente filtra de forma inteligente los resultados externos irrelevantes para que las respuestas se adapten a las políticas privadas.

  • El agente dará respuestas claras y limitadas con información parcial cuando esté disponible o una negativa clara cuando falte el contexto.

Caso de uso: creación de un agente de consulta de pólizas de seguros confiable

Este tutorial es una demostración de la creación de un agente de consulta de pólizas de seguros diseñado para analizar documentos de pólizas (un folleto en PDF) y responder con precisión a las consultas de los usuarios. Utilizamos modelos de IBM Granite y LangChain para crear el agente con pasos sólidos de recuperación y verificación que garantizan respuestas de alta calidad y con restricciones de fuente.

Comprendamos cómo se aplican los principios clave de RAG confiable en nuestro caso de uso.

Aplicación de principios clave

Base de conocimientos interna (PDF): la principal fuente de información del agente es el PDF de su póliza de seguro proporcionada. Convierte este documento en un almacén de vectores en el que se pueden realizar búsquedas.

Búsqueda externa alternativa (Tavily): si la base de conocimientos interna no tiene suficiente información, el agente puede consultar fuentes web externas a través de Tavily. Tavily es un motor de búsqueda creado específicamente para agentes de IA y LLM que da como resultado una recuperación más rápida y en tiempo real a través de su interfaz de programación de aplicaciones (API) para aplicaciones basadas en RAG.

Puntuación contextual: el evaluador de recuperación basado en LLM (que actúa como calificador) proporcionará una puntuación a la relevancia de los elementos recuperados de su PDF interno, al tiempo que garantiza que solo se incluyan los elementos recuperados de alta calidad.

Reescritura de consultas: para las búsquedas web, el agente puede reformular la consulta del usuario para mejorar las posibilidades de encontrar información externa relevante.

Verificación de la fuente: una verificación impulsada por LLM evalúa si los resultados de búsqueda web externos son relevantes para una póliza de seguro privado, filtrando información general o detalles sobre programas de estado público (como Medi-Cal). Esta función evita la generación de respuestas engañosas y permite la autocorrección, lo que ayuda a refinar el conocimiento.

Generación restringida: la instrucción final del LLM le indica estrictamente que use solo el contexto proporcionado, ofrezca respuestas exactas, indique cuándo la información no está disponible o proporcione respuestas parciales con limitaciones explícitas. Esta función mejora la adaptabilidad y confiabilidad de las respuestas generadas.

Requisitos previos

Necesita una cuenta de IBM® Cloud para crear un proyecto watsonx.ai. Asegúrese de tener acceso tanto a su clave de API watsonx como a su ID de proyecto. También necesitará una clave API para Tavily IA para las capacidades de búsqueda web.

Pasos

Paso 1: Configuración del entorno

Si bien puede elegir entre varias herramientas, este tutorial lo guiará a través de cómo configurar una cuenta de IBM mediante un Jupyter Notebook.

  1. Inicie sesión en watsonx.ai mediante su cuenta de IBM Cloud.
  2. Cree un proyecto watsonx.ai. Puede obtener su ID de proyecto desde dentro de su proyecto. Haga clic en la pestaña Administrar. Luego, copie el ID del proyecto de la sección Detalles de la página General. Necesita este ID para este tutorial.
  3. Cree un Jupyter Notebook.

Este paso abre un entorno de notebook donde puede copiar el código de este tutorial. También puede descargar este Notebook en su sistema local y cargarlo en su proyecto watsonx.ai como activo. Para ver más tutoriales de Granite, consulte la comunidad de IBM Granite. Este tutorial también está disponible en Github.

Paso 2. Configuración del servicio de tiempo de ejecución de watsonx.ai y la clave de API

  1. Cree una instancia de servicio watsonx.ai Runtime (elija el plan Lite, que es una instancia gratuita).
  2. Genere una clave (API) de interfaz de programación de aplicaciones.
  3. Asocie el servicio de watsonx.ai Runtime al proyecto que creó en watsonx.ai.

Paso 3. Instalación de los paquetes

Para trabajar con la infraestructura LangChain e integrar IBM WatsonxLLM, necesitamos instalar algunas bibliotecas esenciales. Comencemos por instalar los paquetes necesarios. Este conjunto incluye langchain para el marco RAG, langchain-ibm para la integración de watsonx, faiss-cpu para el almacenamiento vectorial eficiente, PyPDF2 para procesar archivos PDF, sentence-transformers para obtener una incorporación y requests para llamadas a la API web. Estas bibliotecas son críticas para aplicar soluciones de machine learning y PLN.

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

Nota: No se requiere GPU, pero la ejecución puede ser más lenta en sistemas basados en CPU. Este paso abre un entorno de notebook donde puede copiar el código de este tutorial. Este tutorial también está disponible en GitHub.

Paso 3: Importación de las bibliotecas necesarias

A continuación, importe todos los módulos necesarios y proporcione de forma segura sus claves API para watsonx y Tavily, junto con su ID de proyecto watsonx.

# 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 ayuda a trabajar con el sistema operativo.

io permite trabajar con flujos de datos.

getpass utiliza una forma segura de capturar información confidencial, como claves API, y no muestra la entrada en la pantalla.

PyPDF2.PdfReader permite la extracción de contenido de archivos PDF.

langchain_ibm.WatsonxLLM nos permite utilizar IBM watsonx Granite LLM fácilmente dentro de la infraestructura LangChain.

langchain.embeddings.HuggingFaceEmbeddings toma un modelo HuggingFace y genera las incorporaciones textuales que son importantes para la búsqueda semántica.

langchain.vectorstores.FAISS es una biblioteca para el almacenamiento eficiente de vectores y la búsqueda de similitudes que nos permite crear un índice vectorial y consultarlo.

langchain.text_splitter.RecursiveCharacterTextSplitter ayuda a dividir grandes componentes de texto en los fragmentos más pequeños necesarios para procesar documentos que de otro modo no cabrían en la memoria.

langchain.schema.Document representa una unidad arbitraria de texto con metadatos asociados, lo que la convierte en un bloque de construcción en langchain.

 se utiliza para realizar solicitudes HTTP externas a las API.

botocore.client.Config es una clase de configuración utilizada para definir los ajustes de configuración para un cliente de AWS/IBM Cloud Object Storage.

ibm_boto3 es el SDK de IBM Cloud Object Storage para Python que ayuda a interactuar con el cloud object storage.

langchain.prompts.PromptTemplate ofrece una forma de crear instrucciones reutilizables y estructuradas para modelos de lenguaje.

langchain.tools.BaseTool es la clase base a partir de la cual se crean herramientas personalizadas que se pueden dar a los agentes de LangChain para su uso.

Este paso configura todas las herramientas y módulos que necesitamos para procesar texto, crear incorporaciones, almacenarlos en una base de datos vectorial e interactuar con IMB watsonx LLM. Establece todas las partes necesarias para crear un sistema RAG del mundo real, capaz de obtener, consultar y buscar una variedad de tipos de datos.

Paso 5. Cargar y procesar un PDF desde IBM® Cloud Object Storage

En este paso, cargaremos el PDF de la póliza de seguro desde IBM Cloud Object Storage. El código lee el PDF, lee el contenido del texto y divide el texto en fragmentos más pequeños y manejables. Estos fragmentos se convierten en incrustaciones numéricas y se almacenan en un almacén de vectores FAISS que nos prepara para la búsqueda de similitud semántica posterior en el contexto local para optimizar los resultados de recuperación.

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.cliente permite al cliente interactuar con IBM Cloud Object Storage.

Bucket es el nombre del depósito de cloud object storage que contiene el PDF.

object_key es el nombre del PDF en el depósito de almacenamiento de objetos en la nube.

cos_client.get_object(...).read() recupera el contenido del archivo PDF en cloud object storage como bytes.

io.BytesIO convierte los bytes sin procesar de PDF en un flujo binario en memoria en un formato que puede utilizar PdfReader.

PdfReader crea un objeto que puede analizar y extraer texto del PDF.

page.extract_text() extrae el texto de una sola página en el PDF.

RecursiveCharacterTextSplitter está configurado para dividir el texto extraído en fragmentos de 500 caracteres con una superposición de 50 caracteres, manteniendo así todo en contexto.

splitter.split_text(texto) ejecuta la división de todas las páginas del texto PDF en fragmentos más pequeños.

HuggingFaceEmbeddings carga un modelo transformador de oraciones que ha sido entrenado previamente para convertir los fragmentos de texto en representaciones vectoriales densas.

FAISS.from_texts(chunks, incorporaciones) crea un índice FAISS en memoria que permite buscar fragmentos de texto por sus similitudes semánticas.

Este paso maneja la ingesta completa de un documento PDF desde la nube hasta texto listo para LLM y una indexación cómoda para la recuperación en tiempo real.

Paso 6. Iniciar el LLM y las herramientas

En este paso, configurará IBM Granite LLM para impulsar el razonamiento de su agente y lo integrará con la función de búsqueda web de Tavily. Los parámetros del LLM están configurados para respuestas fácticas y estables.

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 instancia el contenedor LLM para IBM® watsonx, lo que permite la interacción con los modelos Granite.

model_id="ibm/granite-3-2b-instruct" es el modelo IBM Granite (un modelo de instrucción de 2700 millones de parámetros) diseñado para tareas de IA generativa basadas en instrucciones.

La clase TavilySearch(BaseTool) define una herramienta LangChain personalizada para realizar búsquedas web mediante la API de Tavily.

tavily_tool = TavilySearch() crea una instancia ejecutable de la herramienta de búsqueda Tavily personalizada.

Cuando inicializamos watsonxLLM, los valores url, apikeyproject_id de nuestras credenciales configuradas previamente se pasan para autenticar y conectarse al servicio. Sus parámetros, como "max_new_tokens": 300, limitan la longitud de la respuesta y "temperature": 0.2 controla la creatividad de salida, favoreciendo Resultados más deterministas.

La definición de la clase TavilySearch incluye una descripción de su función. Su lógica está contenida dentro del método def _run(self, query: str). En este método, realizamos una solicitud HTTP POST al endpoint de la API de Tavily, que incluye TAVILY_API_KEY y la consulta de búsqueda en la carga útil de JSON. Luego verificamos si hay algún error HTTP con response.raise_for_status() y analice la respuesta JSON para acceder al fragmento de contenido del primer resultado de búsqueda.

Este paso configura el modelo de lenguaje para la generación de texto e incluye una herramienta de búsqueda web externa como una forma de aumentar el conocimiento del modelo de lenguaje.

Paso 7. Definir plantillas de instrucciones y funciones auxiliares

Este paso define las diversas plantillas de instrucciones que guían el comportamiento del LLM en diferentes etapas del proceso RAG. Este enfoque incluye instrucciones para calificar la relevancia de los fragmentos de documentos internos, reescribir las consultas de los usuarios para mejorar la búsqueda en la web y una instrucción crítica para Verify la fuente de los Resultados de la búsqueda en la web. También se definen funciones auxiliares para puntuar fragmentos y recuperarlos del almacén de vectores.

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

Este paso define las diversas plantillas de instrucciones que guían el comportamiento del LLM en diferentes etapas del proceso RAG. Instrucciones para puntuar la relevancia de los fragmentos de documentos internos, reescribir las consultas de los usuarios para mejorar la búsqueda web y una nueva instrucción crítica para verificar que se incluyen los resultados de búsqueda web. También se definen funciones auxiliares para puntuar fragmentos y recuperarlos del almacén de vectores.

PromptTemplate.from_template es una función de utilidad de LangChain para crear una plantilla reutilizable para construir instrucciones.

Scoring_prompt_template define una instrucción que indica al LLM que actúe como evaluador y asigne una puntuación de relevancia (0-5) a un fragmento de contexto específico basado en una pregunta.

rewrite_prompt_template define una instrucción que guía al LLM para mejorar o aclarar la pregunta original de un usuario para la búsqueda.

CONTEXT_SOURCE_VERIFICATION_PROMPT define una instrucción que indica al LLM que verifique si un fragmento de texto (por ejemplo, de una búsqueda web) proviene de un contexto de política privada o de una fuente general o pública.

def score_chunks(chunks, query) define una función que toma una lista de fragmentos de texto y una consulta, luego utiliza el LLM para calificar la relevancia de cada fragmento.

def retrieve_from_vectorstore(query) define una función para recuperar los documentos más similares del almacén de vectores FAISS.

Dentro de la función score_chunks, se inicializa una lista vacía puntuada. Para cada fragmento, la plantilla Scoring_prompt_template se formatea con la consulta y el fragmento específicos. Esta instrucción formateada se envía al LLM y la respuesta se elimina. La función intenta extraer la puntuación entera (una puntuación binaria si se simplifica a relevante o no relevante) identificando la línea "Score:" en la respuesta del modelo. El fragmento junto con su puntuación analizada o predeterminada se agrega a la lista puntuada. Esta parte del sistema actúa como evaluador o calificador de recuperación.

La función retrieve_from_vectorstore implementa una vectorstore.similarity_search para encontrar los 8 fragmentos de documentos más relevantes basados en la consulta y recupera el contenido de la página de estos objetos LangChain Document recuperados.

Este paso crea el andamiaje conceptual para el sistema RAG correctivo para que tanto el LLM evalúe el contexto como la forma de recuperar el conocimiento de fuentes de conocimiento internas y externas.

Paso 8. Implementar la lógica correctiva de RAG

La recuperación inicial es la función que escanea el almacén de vectores del PDF.

La puntuación de contexto toma los fragmentos de PDF que se han recuperado para obtener una puntuación de contexto según su relevancia.

Recurra a Tavily si no hay suficiente contexto relevante del PDF, luego consulta Tavily (búsqueda web).

La verificación de la fuente es un paso impulsado por LLM que verifica si los resultados de Tavily son relevantes para una política privada antes de usarlos. Esta función evita respuestas engañosas de los programas de salud pública.

Reescritura de consultas y segunda búsqueda de Tavily Si todavía no hay un buen contexto, reescribe la consulta e intenta la búsqueda de Tavily nuevamente.

La decisión final cuando hay algún contexto relevante, se envía al LLM con una instrucción (estricta) para crear la respuesta. Si no hay un contexto relevante después de todos los intentos viables, envía una denegación cortés.

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

La primera pasada del parámetro policy_context_keywords le permite agregar términos específicos de su póliza (por ejemplo, su nombre, aseguradora) para ayudar a limitar las búsquedas de Tavily.

MIN_CONTEXT_LENGTH define la longitud mínima aceptable del contexto recuperado.

SIMILARITY_THRESHOLD define la puntuación mínima de relevancia que debe tener un fragmento para ser considerado "bueno".

def corrective_rag(...) define la función principal que Orquesta el flujo de trabajo correctivo de RAG.

La función corrective_rag comienza con la creación de retrieved_context_pieces para recopilar el contexto relevante. Primero obtiene y puntúa chunks_from_vectorstore del almacén de vectores PDF en función de la consulta y, a continuación,Score_chunks_vector evalúa su relevancia mediante el uso del modelo de lenguaje. Solo se conservan good_chunks_vector que cumplen con SIMILARITY_THRESHOLD. Luego, el contexto_actual se compila a partir de estas piezas.

Si current_context está por debajo de MIN_CONTEXT_LENGTH, el sistema intenta una búsqueda web. Construye tavily_search_query, incorporando potencialmente policy_context_keywords. Se realiza una búsqueda directa (tavily_context_direct ). Fundamentalmente, se crea un check_prompt y se envía al LLM para determinar si el resultado de la búsqueda web (is_relevant_source) proviene de una política privada en lugar de un programa público. Si es SÍ, se agrega el contexto.

Si el contexto sigue siendo insuficiente, el sistema se prepara para reescribir la consulta. Utiliza rewrite_prompt para obtener una consulta mejorada del LLM y, a continuación, realiza una segunda búsqueda web (tavily_context_rewrite). Este nuevo contexto también se somete a la misma verificación de fuente.

Finalmente, if len(current_context.strip()) == 0 es una última verificación. Si no se encuentra ningún contexto relevante después de todos los intentos, se devuelve un mensaje de rechazo predefinido. De lo contrario, se crea un final_prompt con todo el contexto verificado y se envía al modelo de lenguaje para generar su respuesta final.

Toda la función corrective_rag maneja en detalle las funciones de recuperación, puntuación y verificación por etapas del RAG correctivo. Permite la actualización constante de la base de conocimientos y el flujo de conocimientos, y aporta el beneficio de respuestas sólidas y conscientes del contexto.

Paso 9. Probar el sistema

Finalmente, ejecute la función corrective_rag con una consulta de muestra. Es crucial proporcionar policy_context_keywords que sean específicas para su documento PDF. Estas palabras clave ayudarán a que la búsqueda web de Tavily sea más relevante para su política real, evitando que la información general o del programa de estado público contamine su contexto.

Observe las declaraciones de impresión para conocer la longitud del contexto y los resultados de la verificación para comprender el flujo de información.

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 estado", "Care estado Insurance"] define una lista de palabras clave que son relevantes para la póliza de seguro cargada, lo que ayuda a reducir los Resultados de búsqueda web.

query = "..." define la pregunta particular que un usuario podría hacer.

result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) llama a la función principal corrective_rag y pasa la consulta del usuario y las palabras clave específicas de la política para comenzar todo el proceso de RAG.

print("\n RESPUESTA FINAL (...)") muestra un encabezado claro antes de imprimir la respuesta generada.

print(result) genera los resultados devueltos por el sistema corrective_rag.

Este paso muestra cómo invocar el sistema RAG correctivo completo con una consulta de muestra y palabras clave, demostrando su funcionalidad de extremo a extremo en un escenario del mundo real.

Conclusiones clave

La RAG correctiva implementó una base de conocimientos de PDF interna totalmente coordinada con un servicio externo (Tavily) para recuperar información completa para solicitudes complejas.

Evaluó y filtró con precisión a través del contexto recuperado mediante el uso de puntuación basada en LLM y la verificación de fuentes críticas para garantizar que se utilice información válida y confiable.

El sistema demostró la capacidad de mejorar la búsqueda externa reescribiendo de forma inteligente las consultas de los usuarios para solicitar información más específica y de mayor calidad.

Al utilizar la generación restringida, comúnmente se generaba una respuesta confiable y contextualmente precisa y el sistema se negaba cortésmente a responder si no había suficiente información verificada conocida.

Este ejemplo demostró cómo se pueden utilizar LangChain e IBM Granite LLM en watsonx para desarrollar aplicaciones potentes y confiables basadas en IA confiable en dominios confidenciales, como hacer preguntas sobre pólizas de seguros.

Soluciones relacionadas
Agentes de IA para empresas

Cree, implemente y gestione poderosos asistentes y agentes de IA que automaticen flujos de trabajo y procesos con IA generativa.

    Explore watsonx Orchestrate
    Soluciones de agentes de IA de IBM

    Construya el futuro de su empresa con soluciones de IA en las que pueda confiar.

    Explorar las soluciones de agentes de IA
    Servicios de IA de IBM Consulting

    Los servicios de IA de IBM Consulting ayudan a reinventar la forma en que las empresas trabajan con IA para la transformación.

    Explorar los servicios de inteligencia artificial
    Dé el siguiente paso

    Ya sea que elija personalizar aplicaciones y habilidades predefinidas o crear y desplegar servicios agénticos personalizados utilizando un estudio de IA, la plataforma IBM watsonx responde a sus necesidades.

    Explore watsonx Orchestrate Explore watsonx.ai