Crie um agente de RAG corretiva usando o IBM Granite e o Tavily

Autora

Jobit Varughese

Technical Content Writer

IBM

Os grandes modelos de linguagem (LLMs) são incrivelmente poderosos, mas seu conhecimento é limitado aos conjuntos de dados de treinamento. Ao responder a perguntas, especialmente sobre informações específicas, em evolução ou proprietárias, os LLMs podem alucinar ou fornecer respostas gerais e irrelevantes. A geração aumentada de recuperação (RAG) ajuda fornecendo ao LLM informações recuperadas relevantes de fontes de dados externas.

No entanto, nem toda RAG é igual. A geração aumentada de recuperação corretiva (cRAG) não se baseia simplesmente na RAG mais tradicional, ela representa uma melhoria significativa. Ele foi projetado para ser mais robusto ao avaliar a qualidade e a relevância dos resultados recuperados. Se o contexto for fraco, irrelevante ou de uma fonte não confiável, a cRAG tenta encontrar informações melhores por meio de ações corretivas ou se recusa explicitamente a responder, em vez de fabricar uma resposta. Essa técnica torna os sistemas de cRAG mais confiáveis e fidedignos para aplicações críticas, como responder a perguntas relacionadas a políticas.

Documentos relevantes do diagrama

Neste tutorial, você aprenderá a construir um sistema de RAG corretiva (cRAG) robusto usando os modelos IBM Granite no Watsonx e no LangChain. Frameworks semelhantes, como o LlamaIndex ou o LangGraph, também podem ser utilizados para criar fluxos de RAG complexos com nós distintos. Técnicas como ajuste fino podem melhorar ainda mais o desempenho específico do LLM para a RAG específica do domínio. LLMs como os da OpenAI (por exemplo, modelos GPT como o ChatGPT) também são escolhas populares para esses agentes, embora este tutorial se concentre no IBM Granite.

Aqui, vamos nos concentrar em um caso de uso: responder a perguntas sobre um documento específico de apólice de seguro (um PDF). Este tutorial guiará você na implementação de um algoritmo de RAG sofisticado que:

  • Recupera informações do seu próprio documento PDF.

  • Se os documentos internos não forem suficientes para gerar a resposta, o agente poderá usar uma pesquisa externa na web (Tavily) como alternativa.

  • O agente filtra de forma inteligente os resultados externos irrelevantes para que as respostas sejam adaptadas às políticas privadas.

  • O agente dará respostas claras e limitadas com informações parciais quando disponíveis, ou uma recusa clara quando o contexto estiver faltando.

Caso de uso: criação de um agente de consulta de apólices de seguro confiável

Este tutorial é uma demonstração de criação de um agente de consulta de apólices de seguros projetado para analisar documentos de políticas (um folheto em PDF) e responder com precisão às consultas dos usuários. Usamos os modelos IBM Granite e o LangChain para construir o agente com etapas robustas de recuperação e verificação, garantindo respostas de alta qualidade com fontes limitadas.

Vamos entender como os princípios fundamentais da RAG confiável se aplicam ao nosso caso de uso.

Aplicação dos princípios fundamentais

Base de conhecimento interna (PDF): a principal fonte da verdade do agente é o PDF da apólice de seguro fornecido. Ele converte esse documento em um armazenamento de vetores pesquisável.

Fallback de pesquisa externa (Tavily): se a base de conhecimento interna não tiver informações suficientes, o agente poderá consultar fontes externas da web por meio do Tavily. O Tavily é um mecanismo de busca criado especificamente para agentes de IA e LLMs que resulta em resultados mais rápidos e em tempo real por meio de sua interface de programação de aplicativos (API) para aplicações baseadas na RAG.

Pontuação de contexto: o avaliador de recuperação baseado em LLM (agindo como avaliador) fornecerá uma pontuação para a relevância dos itens recuperados do seu PDF interno, garantindo que apenas itens recuperados de alta qualidade sejam incluídos.

Reescrita de consultas: para pesquisas na web, o agente pode reformular a consulta do usuário para melhorar as chances de encontrar informações externas relevantes.

Verificação da fonte: uma verificação impulsionada por LLM avalia se os resultados de pesquisas externas na web são relevantes para uma apólice de seguro privada, filtrando informações gerais ou detalhes sobre programas de integridade pública (como o Medi-Cal). Essa função evita a geração de respostas enganosas e possibilita a autocorreção, auxiliando no refinamento do conhecimento.

Geração restrita: o prompt final para o LLM dá instruções restritas para usar apenas o contexto fornecido, oferecer respostas exatas, informar quando as informações estiverem indisponíveis ou fornecer respostas parciais com limitações explícitas. Essa função aumenta a adaptabilidade e a confiabilidade das respostas geradas.

Pré-requisitos

Você precisa de uma conta do IBM® Cloud para criar um projeto do watsonx.ai . Certifique-se de ter acesso à chave da API do watsonx e ao ID do projeto. Você também precisará de uma chave de API para a IA do Tavily para recursos de pesquisa na web.

Etapas

Etapa 1. Configure seu ambiente

Embora possa escolher entre várias ferramentas, este tutorial vai orientar você em como configurar uma conta da IBM usando um Jupyter Notebook.

  1. Faça login no watsonx.ai usando sua conta do IBM Cloud.
  2. Criar um projeto do watsonx.ai.Você pode obter a ID do projeto a partir de seu projeto. Clique na guia Gerenciar. Em seguida, copie a ID do projeto da seção Detalhes da página Geral. Você precisa dessa ID para este tutorial.
  3. Crie um Jupyter Notebook.

Essa etapa abre um ambiente do Notebook, onde você poderá copiar o código deste tutorial. Ou então, você pode baixar esse Notebook em seu sistema local e carregá-lo como um ativo em seu projeto do watsonx.ai. Para ver mais tutoriais do Granite, consulte a Comunidade IBM Granite. Este tutorial também está disponível no Github.

Etapa 2. Configure o serviço watsonx.ai Runtime e a chave de API

  1. Crie uma instância do serviço watsonx.ai Runtime (escolha o plano Lite, que é uma instância gratuita).
  2. Gere uma chave de interface de programação de aplicativos (API).
  3. Associe o serviço watsonx.ai Runtime ao projeto que você criou no watsonx.ai.

Etapa 3. Instalação dos pacotes

Para trabalhar com o framework LangChain e integrar o IBM WatsonxLLM, precisamos instalar algumas bibliotecas essenciais. Vamos começar instalando os pacotes necessários. Esse conjunto inclui o langchain para o framework de RAG, o langchain-ibm para a integração com o watsonx,  ofaiss-cpu para o armazenamento de vetores eficiente, o PyPDF2 para processar PDFs, os transformadores de frases para obter um embeddingsolicitações para chamadas de APIs da web. Essas bibliotecas são críticas para aplicar soluções de aprendizado de máquina e NLP.

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

Observação: nenhuma GPU é necessária, mas a execução pode ser mais lenta em sistemas baseados em CPU. Essa etapa abre um ambiente do Notebook, onde você poderá copiar o código deste tutorial. Este tutorial também está disponível no GitHub.

Etapa 4. Importe as bibliotecas necessárias

Em seguida, importe todos os módulos necessários e forneça com segurança suas chaves de API para o watsonx e o Tavily, juntamente com sua ID do projeto do 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 ajuda a trabalhar com o sistema operacional.

io permite trabalhar com fluxos de dados.

getpass usa uma maneira segura de capturar informações confidenciais, como chaves de API, e não exibe a entrada na tela.

PyPDF2.PdfReader permite a extração de conteúdo de PDFs.

langchain_ibm.WatsonxLLM nos permite usar o IBM Watsonx Granite facilmente dentro do framework do LangChain.

langchain.embeddings.HuggingFaceEmbeddings utiliza um modelo HuggingFace e gera os embeddings textuais importantes para a pesquisa semântica.

langchain.vectorstores.FAISS é uma biblioteca para armazenamento vetorial eficiente e pesquisa por similaridade que nos permite construir um índice vetorial e consultá-lo.

langchain.text_splitter.RecursiveCharacterTextSplitter ajuda a dividir grandes constituintes de texto em fragmentos menores necessários para processar documentos que, de outra forma, não caberiam na memória. 

langchain.schema.Document representa uma unidade arbitrária de texto com metadados associados, tornando-o um bloco de construção no langchain.

requests é usado para fazer solicitações HTTP externamente às APIs.

botocore.client.Config é uma classe de configuração usada para definir definições de configuração para um cliente AWS/IBM Cloud Object Storage.

ibm_boto3 é o IBM Cloud Object Storage SDK for Python que ajuda a interagir com o Cloud Object Storage.

langchain.prompts.PromptTemplate oferece uma maneira de criar prompts reutilizáveis e estruturados para modelos de linguagem.

langchain.tools.BaseTool é a classe base a partir da qual você cria ferramentas personalizadas que podem ser fornecidas aos agentes do LangChain para uso.

Essa etapa configura todas as ferramentas e módulos necessários para processar texto, criar embeddings, armazená-las em um banco de dados de vetores e interagir com o IBM watsonx LLM. Ele estabelece todas as partes necessárias para criar um sistema de RAG do mundo real, capaz de fornecer, consultar e pesquisar uma variedade de tipos de dados.

Etapa 5. Carregue e processe um PDF do IBM Cloud Object Storage

Nesta etapa, carregaremos o PDF da apólice de seguro do IBM Cloud Object Storage. O código lê o PDF, lê o conteúdo do texto e divide o texto em fragmentos menores e gerenciáveis. Esses fragmentos são convertidos em embeddings e armazenados em um armazenamento de vetores FAISS que nos prepara para pesquisas de similaridade semântica posteriores no contexto local para otimizar os resultados.

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

O ibm_boto3.client permite que o cliente interaja com o IBM Cloud Object Storage.

Bucket é o nome do bucket do Cloud Object Storage que contém o PDF.

object_key é o nome do PDF no bucket de armazenamento de objetos no Cloud Object Storage.

cos_client.get_object(...).read() recupera o conteúdo do arquivo PDF no Cloud Object Storage como bytes.

io.BytesIO converte os bytes brutos do PDF em um fluxo binário na memória em um formato que pode ser usado pelo PdfReader.

PdfReader cria um objeto que pode analisar e extrair texto do PDF.

page.extract_text() extrai o texto de uma única página no PDF.

RecursiveCharacterTextSplitter é configurado para dividir o texto extraído em fragmentos de 500 caracteres com uma sobreposição de 50 caracteres, mantendo, assim, tudo em contexto.

splitter.split_text(text) executa a divisão de todas as páginas do texto PDF em fragmentos menores.

HuggingFaceEmbeddings carrega um modelo de transformador de frases que foi pré-treinado para converter os fragmentos de texto em representações vetoriais densas.

FAISS.from_texts(chunks, embeddings) constrói um índice FAISS na memória que permite que fragmentos de texto sejam pesquisáveis por suas similaridades semânticas.

Esta etapa lida com a ingestão completa de um documento PDF, da nuvem ao texto pronto para LLM e à indexação confortável para recuperação em tempo real.

Etapa 6. Inicialize o LLM e as ferramentas

Nesta etapa, você configurará o IBM Granite LLM para conduzir o raciocínio do seu agente e integrá-lo à função de pesquisa na web do Tavily. Os parâmetros do LLM são configurados para respostas factuais e estáveis.

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 o wrapper do LLM para o IBM watsonx, permitindo a interação com os modelos do Granite.

model_id="ibm/granite-3-2b-instruct" é o modelo IBM Granite (um modelo de instrução de 2,7 bilhões de parâmetros) projetado para tarefas de IA generativa baseadas em instruções.

class TavilySearch(BaseTool) define uma ferramenta LangChain personalizada para realizar pesquisas na web usando a API do Tavily.

tavily_tool = TavilySearch() cria uma instância executável da ferramenta de pesquisa personalizada Tavily.

Quando inicializamos o watsonxLLM, os valores de urlapikeyproject_id de nossas credenciais configuradas anteriormente são passados para autenticação e conexão com o serviço. Seus parâmetros, como "max_new_tokens": 300, limitam o comprimento da resposta e "temperature": 0.2 controlam a criatividade da saída, favorecendo resultados mais determinísticos.

A definição da classe TavilySearch inclui uma descrição de sua função. Sua lógica está contida no método def _run(self, Query: str). Nesse método, fazemos uma solicitação HTTP POST para o endpoint da API do Tavily, incluindo o TAVILY_API_KEY e a consulta de pesquisa no payload JSON. Em seguida, verificamos se há algum erro de HTTP com response.raise_for_status() e analisamos a resposta de JSON para acessar o trecho de conteúdo do primeiro resultado da pesquisa.

Essa etapa configura o modelo de idioma para geração de texto e inclui uma ferramenta externa de pesquisa na web como uma maneira de aumentar o conhecimento do modelo de idioma.

Etapa 7. Defina modelos de prompts e funções auxiliares

Esta etapa define os vários modelos de prompt que orientam o comportamento do LLM em diferentes estágios do processo de RAG. Essa abordagem inclui prompts para pontuar a relevância de fragmentos de documentos internos, reescrever consultas de usuários para melhorar a pesquisa na web e um prompt crítico para verificar a fonte de resultados de pesquisas na web. Funções auxiliares para pontuar fragmentos e recuperá-los do armazenamento de vetores também são definidas.

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

Esta etapa define os vários modelos de prompt que orientam o comportamento do LLM em diferentes estágios do processo de RAG. Solicitações para pontuar a relevância de fragmentos de documentos internos, reescrever consultas de usuários para melhorar a pesquisa na web e um prompt crítico para verificar se a fonte dos resultados da pesquisa na web está incluída. Funções auxiliares para pontuar fragmentos e recuperá-los do armazenamento de vetores também são definidas.

PromptTemplate.from_template é uma função de utilitário do LangChain para criar um modelo reutilizável para construir prompts.

score_prompt_temlate define um prompt que instrui o LLM a atuar como avaliador e atribuir uma pontuação de relevância (0 a 5) a um fragmento de contexto específico com base em uma pergunta.

rewrite_prompt_template define um prompt que orienta o LLM a melhorar ou tornar a pergunta original de um usuário mais clara para pesquisa.

CONTEXT_SOURCE_VERIFICATION_PROMPT define um prompt que instrui o LLM a verificar se um trecho de texto (por exemplo, de uma pesquisa na web) é de um contexto de política privada ou de uma fonte geral ou pública.

def score_chunks(chunks, query) define uma função que usa uma lista de fragmentos de texto e uma consulta, em seguida, usa o LLM para pontuar a relevância de cada fragmento.

def retrieve_from_vectorstore(query) define uma função para recuperar os documentos mais semelhantes do armazenamento de vetores FAISS.

Dentro da função score_chunks , uma lista de pontuações vazia é inicializada. Para cada fragmento, o score_prompt_template é formatado com a consulta e o fragmento específicos. Esse prompt formatado é, então, enviado ao LLM, e a resposta é removida. A função tenta extrair a pontuação inteira (uma pontuação binária, se simplificada para relevante ou não relevante) identificando a linha "Score:" na resposta do modelo. O fragmento, juntamente com sua pontuação analisada ou padronizada, são então adicionados à lista de pontuações. Essa parte do sistema atua como avaliador ou classificador de recuperação.

A função retrieve_from_vectorstore implementa um vetorstore.similarity_search para encontrar os oito fragmentos do documento mais relevantes com base na consulta e recuperar o page_content desses objetos de documentos do LangChain recuperados.

Esta etapa constrói a estrutura conceitual para o sistema de RAG corretiva, de modo que o LLM avalie o contexto e como recuperar o conhecimento de fontes de conhecimento internas e externas.

Etapa 8. Implemente a lógica da RAG corretiva

A recuperação inicial é a função que verifica o armazenamento de vetores do PDF.

A pontuação de contexto considera os fragmentos do PDF recuperadas para a pontuação de contexto de acordo com a relevância.

Fallback para o Tavily se não houver contexto relevante suficiente no PDF, ele consulta o Tavily (pesquisa na web).

A verificação da fonte é uma etapa impulsionada por LLM que verifica se os resultados do Tavily são relevantes para uma política privada antes de usá-los. Essa função evita respostas enganosas de programas de saúde pública.

Reescrita da consulta e segunda pesquisa do Tavily ,se ainda não houver um bom contexto, ele reescreve a consulta e tenta a pesquisa do Tavily novamente.

Decisão final ,quando há algum contexto relevante é enviada ao LLM com um prompt (rigoroso) para criar a resposta. Se não houver contexto relevante após todas as tentativas viáveis, ele envia uma negação educação.

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

A primeira passagem do parâmetro policy_context_keywords permite que você adicione termos específicos da sua apólice (por exemplo, nome, seguradora) para ajudar a restringir as pesquisas para o Tavily.

MIN_CONtext_LENGTH define o comprimento mínimo aceitável do contexto recuperado.

SIMILARITY_THRESHOLD define a pontuação mínima de relevância que um fragmento deve ter para ser considerado "bom".

def corrective_rag(...) define a função principal que orquestra todo o fluxo de trabalho da RAG corretiva RAG.

A função corrective_rag começa criandoretrieved_context_pieces para reunir o contexto relevante. Primeiro, busca e pontua chunks_from_vectorstore do armazenamento de vetores PDF com base na consulta e, em seguida, scored_chunks_vector avalia sua relevância usando o modelo de linguagem. Somente good_chunks_vector que atendem a SIMILARITY_THRESHOLD são mantidos. O current_context é, então, compilado a partir dessas partes.

Se current_context estiver abaixo de MIN_CONTEXT_LENGTH, o sistema tentará uma pesquisa na web. Ele constrói tavily_search_query, podendo incorporar policy_context_keywords. Uma pesquisa direta (tavily_context_direct) é realizada. Crucialmente, um verification_prompt é criado e enviado ao LLM para determinar se o resultado da pesquisa na web (is_relevant_source) é de uma política privada em vez de um programa público. Se for SIM, o contexto será adicionado.

Se o contexto permanecer insuficiente, o sistema se prepara para reescrever a consulta. Ele usa rewrite_prompt para obter uma improved_query do LLM e, em seguida, realiza uma segunda pesquisa na web (tavily_context_rewritten). Esse novo contexto também passa pela mesma verificação da fonte.

Finalmente, if len(current_context.strip()) == 0 é uma última verificação. Se nenhum contexto relevante for encontrado após todas as tentativas, uma mensagem de recusa predefinida será retornada. Caso contrário, um final_prompt é criado com todo o contexto verificado e enviado ao modelo de linguagem para gerar sua resposta final.

Toda a função corretiva_rag lida com as funções de recuperação, pontuação e verificação em estágios da RAG corretiva em detalhes. Ela permite a atualização constante da base de conhecimento e do fluxo de conhecimento e traz o benefício de respostas robustas e contextualmente conscientes.

Etapa 9. Teste o sistema

Por fim, execute a função corrective_rag com uma consulta de amostra. É fundamental fornecer as policy_context_keywords específicas do seu documento PDF. Essas palavras-chave ajudarão a pesquisa na web do Tavily a se tornar mais relevante para sua política real, evitando que informações gerais ou de programas de integridade pública poluam seu contexto.

Observe as instruções de impressão para o comprimento do contexto e os resultados da verificação para entender o fluxo de informações.

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"] define uma lista de palavras-chave relevantes para a apólice de seguro carregada, ajudando a restringir os resultados da pesquisa na web.

query = "..." define a pergunta específica que um usuário pode fazer.

result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) chama a função principal corrective_rag e passa a consulta do usuário e as palavras-chave específicas da política para iniciar todo o processo de RAG.

print("\n FINAL ANSWER (...)") exibe um cabeçalho limpo antes de imprimir a resposta gerada.

print(result) gera a resposta final retornada pelo sistema corrective_rag.

Esta etapa mostra como invocar o sistema de RAG corretiva completo com uma consulta de exemplo e palavras-chave, demonstrando sua funcionalidade de ponta a ponta em um cenário do mundo real.

Principais conclusões

A RAG corretiva implementada coordenou totalmente uma base de conhecimento interna de PDF com um serviço externo (Tavily) para recuperar informações abrangentes para solicitações complexas.

Ele avaliou e filtrou com precisão o contexto recuperado usando pontuação baseada em LLM e verificação crítica de fontes para garantir que informações válidas e confiáveis estejam sendo usadas.

O sistema demonstrou a capacidade de melhorar a pesquisa externa reescrevendo de forma inteligente as consultas dos usuários para solicitar informações mais direcionadas e de maior qualidade.

Ao usar a geração restrita, era comum gerar uma resposta confiável e contextualmente precisa, e o sistema recusava polidamente a responder se não houvesse informações verificadas conhecidas o suficiente.

Este exemplo demonstrou como os LLMs do LangChain e do IBM Granite no watsonx podem ser usados para desenvolver aplicativos poderosos e de IA confiável em domínios sensíveis, como fazer perguntas sobre apólices de seguro.

Soluções relacionadas
Agentes de IA para empresas

Crie, implemente e gerencie assistentes e agentes de IA potentes que automatizam fluxos de trabalho e processos com a IA generativa.

    Explore o watsonx Orchestrate
    Soluções de agentes de IA da IBM

    Construa o futuro do seu negócio com soluções de IA em que você pode confiar.

    Explore soluções de agentes de IA
    Serviços de IA do IBM® Consulting

    Os serviços de IA da IBM Consulting ajudam a reinventar a forma como as empresas trabalham com IA para gerar transformação.

    Explore os serviços de inteligência artificial
    Dê o próximo passo

    Se você optar por personalizar aplicativos e habilidades criados previamente ou criar e implementar serviços agênticos personalizados usando um estúdio de IA, a plataforma IBM watsonx tem aquilo de que você precisa.

    Explore o watsonx Orchestrate Explore o watsonx.ai