大型语言模型 (LLM) 功能非常强大,但其知识仅限于训练 数据集。在回答问题时,尤其是关于特定、不断发展或专有信息的问题时,LLMLLM 可能会产生幻觉或提供笼统、不相关的答案。 检索增强生成 (RAG) 通过向 LLM 提供来自外部数据源的相关检索信息来提供帮助。
然而,并非所有的 RAG 都效果相同。纠正性检索增强生成 (cRAG) 并不是简单地构建在传统 RAG 之上,而是代表了一种重大改进。它旨在通过评估检索结果的质量和相关性来变得更加稳健。如果上下文薄弱、不相关或来源不可信,cRAG 会尝试通过纠正措施来找到更好的信息,或明确拒绝回答,而不是编造响应。这种技术使得 cRAG 系统在回答政策相关问题等关键应用中更加可靠和值得信赖。
在本教程中,您将学习如何利用 Watsonx 上的 IBM Granite 模型和 LangChain 构建一个稳健的纠正性 RAG 系统。类似的框架如 LlamaIndex 或 LangGraph 也可用于构建具有不同节点的复杂 RAG 流程。 微调 等技术可以进一步提高特定领域 RAG 的 LLM 性能。虽然 OpenAI 的 LLM( GPT 模型,如 ChatGPT)也是此类智能体的热门选择,不过本教程重点介绍 IBM Granite。
在这里,我们将关注一个用例:回答关于特定保单文档 (PDF) 的问题。本教程将指导您实现一个复杂的 RAG 算法,该算法能够:
从您自己的 PDF 文档中检索信息 。
如果内部文档不足以生成答案,智能体可以使用外部网络搜索 (Tavily) 作为后备。
智能体智能地过滤掉不相关的外部结果,使答案针对私有保单。
当有部分信息可用时,智能体会给出清晰、有限制的回答;当上下文缺失时,则会明确拒绝。
本教程演示了如何构建一个保单查询智能体,该智能体旨在分析保单文档(PDF 手册)并准确回答用户查询。我们使用 IBM Granite 模型和 LangChain 来构建该智能体,并通过稳健的检索和验证步骤确保高质量、受限于信息来源的答案。
让我们了解可靠的 RAG 的关键原则如何应用于我们的用例。
内部知识库 (PDF):智能体的核心知识来源是用户提供的保险单 PDF 文档。它将此文档转换为可搜索的向量存储。
外部搜索回退 (Tavily):如果内部知识库信息不足,智能体可以通过 Tavily 咨询外部网络资源。Tavily 是专为 AI 智能体和 LLM 构建的搜索引擎,通过其应用程序编程接口 (API) 为基于 RAG 的应用程序实现更快、实时的检索。
上下文评分: 基于 LLM 的检索评估器(充当评分器)将为从内部 PDF 检索到的项目相关性提供分数,同时确保只包含高质量的检索项。
查询重写: 对于网络搜索,智能体可以重新表述用户的查询,以提高找到相关外部信息的几率。
来源验证:LLM 支持的检查会评估外部网络搜索结果是否与私有保单相关,过滤掉一般信息或关于公共健康计划(如 Medi-Cal)的结果。此功能可防止产生误导性答案,并实现自我纠正,有助于知识精炼。
受限生成: 给 LLM 的最终提示严格指示其仅使用提供的上下文,提供精确答案,说明信息不可用的情况,或在有明确限制的情况下提供部分答案。此功能增强了生成响应的适应性和可靠性。
您需要一个 IBM® Cloud 帐户 才能创建 watsonx.ai 项目。确保您有权访问 watsonx API 密钥和项目 ID。您还需要一个 Tavily AI 的 API 密钥以获得网络搜索能力。
虽然您可以选择多种工具,本教程将引导您如何设置 IBM 帐户以使用 Jupyter Notebook。
此步骤将打开一个笔记本环境,可在其中复制本教程中的代码。或者,您可以将此笔记本下载到本地系统并将其作为资产上传到您的 watsonx.ai 项目。要查看更多 Granite 教程,请访问 IBM Granite 社区 。本教程也可在 Github 上找到。
为了使用 LangChain 框架并集成 IBM WatsonxLLM,我们需要安装一些基础库。我们先来安装所需的软件包。这套工具包括用于 RAG 框架的 langchain 、用于 watsonx 集成的 langchain-ibm 、用于高效向量存储的 faiss-cpu 、用于处理 PDF 的 PyPDF2 、用于获取 嵌入 表示的sentence-transformers以及用于 Web API 调用的 requests 。这些库对于应用 机器学习 和 NLP 解决方案至关重要。
注意: 虽然无需 GPU 即可运行,但在纯 CPU 系统上执行速度可能较慢。此步骤将开启一个 Notebook 编程环境,您可在此直接复制并运行教程中的示例代码。本教程也可在 GitHub 上找到。
接下来,请导入所有必需的依赖模块,并安全地配置您的 watsonx 与 Tavily API 密钥以及 watsonx 项目 ID。
os 模块提供操作系统交互功能。
io 模块支持数据流处理操作。
getpass 模块通过安全方式采集 API 密钥等敏感信息,且不会在屏幕显示输入内容。
PyPDF2.PdfReader 组件实现 PDF 文档内容解析与提取。
langchain_ibm.WatsonxLLM 封装器支持在 LangChain 框架内便捷调用 IBM Watsonx Granite LLM。
langchain.embeddings.HuggingFaceEmbeddings 加载预训练的 HuggingFace 模型,以生成语义搜索所需的文本嵌入向量。
langchain.vectorstores.FAISS 提供高效的向量存储与相似度检索能力,用于构建可语义查询的向量索引。
langchain.text_splitter.RecursiveCharacterTextSplitter 可帮助将大段文本拆分为较小的分块,以处理无法放入内存的文档。
langchain.schema.Document 作为 LangChain 基础构件,表征附带元数据的文本单元。
requests 用于向外部 API 发起 HTTP 请求。
botocore.client.Config 是一个配置类,用于定义 AWS/IBM Cloud Object Storage 客户端的配置参数。
ibm_boto3 作为 IBM Cloud Object Storage 的 Python SDK,支持与云存储服务交互。
langchain.prompts.PromptTemplate 提供了一种为语言模型创建可重复使用的结构化提示的方法。
langchain.tools.BaseTool 作为基础工具类,支持构建可集成至 LangChain 智能体的自定义工具模块。
此步骤设置了我们处理文本、创建嵌入、将其存储在矢量数据库中并与 IBM watsonx LLM 交互所需的所有工具和模块。它建立了创建实际 RAG 系统所需的所有部件,能够获取、查询和搜索一系列数据类型。
在此步骤中,我们将从 IBM Cloud Object Storage 加载保单 PDF。代码会读取 PDF、读取文本内容并将文本拆分为较小且可管理的分块。这些分块随后被转换为数值化嵌入向量,并存储于 FAISS 向量数据库中,为后续基于本地上下文的语义相似度检索优化查询结果。
ibm_boto3.client 支持客户端与 IBM Cloud Object Storage 交互。
Bucket 是包含 PDF 的云对象存储存储桶的名称。
object_key 是云存储桶中 PDF 的名称。
cos_client.get_object(...).read() 从云对象存储中以字节流形式获取 PDF 内容。
io.BytesIO 将 PDF 原始字节转换为 PdfReader 可解析的内存二进制流格式。
PdfReader 创建一个可以从 PDF 中解析和提取文本的对象。
page.extract_text() 提取 PDF 中单个页面的文本。
RecursiveCharacterTextSplitter 被配置为将提取的文本拆分为 500 个字符的分块,重叠 50 个字符,从而将所有内容保留在上下文中。
splitter.split_text(text) 将 PDF 文本的所有页面分割成小分块。
HuggingFaceEmbeddings 可加载预训练的句子转换器模型,将文本块编码为稠密向量表示。
FAISS.from_texts(chunks, embeddings) 构建内存式 FAISS 索引,实现基于语义相似度的文本分块检索。
本步骤完整实现了从云端 PDF 文档到 LLM 就绪文本的摄取流程,并建立实时检索所需的优化索引结构。
在此步骤中,您将配置驱动智能体推理的 IBM Granite LLM,并将其与 Tavily 网络搜索功能集成。通过设置 LLM 参数,确保生成事实准确、稳定性高的响应结果。
WatsonxLLM 实例化 IBM watsonx 的 LLM 封装器,支持与 Granite 模型交互。
model_id="ibm/granite-3-2b-instruct" 指定采用 27 亿参数的 IBM Granite 指令微调模型,专为基于指令的生成式 AI 任务设计。
class TavilySearch(BaseTool) 定义基于 Tavily API 执行网络搜索的自定义 LangChain 工具类。
tavily_tool = TavilySearch() 创建可执行的自定义 Tavily 搜索工具实例.
当我们初始化 watsonxLLM 时,之前设置凭据中的 url、 apikey和 project_id 值将被传递,用于服务认证并连接到服务。其参数如 "max_new_tokens": 300 限制响应长度,而 "temperature": 0.2 控制输出创造性,倾向于生成更具确定性的结果。
TavilySearch 类的定义包括对其功能的描述。其逻辑包含在 def _run(self, query: str) 方法中。在此方法中,我们向 Tavily API 端点发出 HTTP POST 请求,其中包括 JSON 有效载荷中的 TAVILY_API_KEY 和搜索查询。我们然后使用 response.raise_for_status() 来验证是否存在任何 HTTP 错误并解析 JSON 响应,以获取首个搜索结果的摘要内容。
此步骤设置用于文本生成的语言模型,并通过集成外部网络搜索工具来扩展模型的知识边界。
此步骤定义各种提示模板,这些模板可在 RAG 流程的不同阶段指导 LLM 的行为。这种方法包括对内部文档分块相关性评分的提示,重写用户查询以实现更好的网络搜索的提示,以及用于验证网络搜索结果来源的新提示。此外还定义了用于对分块进行评分和从向量存储中检索数据的辅助函数。
此步骤定义各种提示模板,这些模板可在 RAG 流程的不同阶段指导 LLM 的行为。对内部文档分块相关性评分的提示,重写用户查询以实现更好的网络搜索的提示,以及用于验证网络搜索结果来源的新提示。此外还定义了用于对分块进行评分和从向量存储中检索数据的辅助函数。
PromptTemplate.from_template 作为 LangChain 工具函数,用于创建可重复使用的模板来构建提示。
scoring_prompt_template 定义提示模板,指导 LLM 扮演评估者角色,根据问题为特定上下文块分配相关性分数(0-5)。
rewrite_prompt_template 定义提示模板,引导 LLM 优化或澄清用户原始查询以提升搜索效果。
CONTEXT_SOURCE_VERIFICATION_PROMPT 定义提示模板,指示 LLM 验证文本片段是否源自私有保单上下文而非通用公开来源。
def score_chunks(chunks, query) 定义函数,用于接收文本分块列表和查询,然后使用 LLM 对每个文本分块的相关性进行评分。
def retrieve_from_vectorstore(query) 定义函数,用于从 FAISS 向量存储检索最相似的文档。
在 score_chunks 函数中,系统初始化空评分列表。对于每个分块, scoring_prompt_template 都会根据特定查询和分块进行格式化。然后将格式化提示发送到 LLM,并解析响应结果。该函数尝试通过识别模型响应中的“Score:”行来提取整型分数(如果简化为相关或不相关,则为二进制分数)。然后,该分块及其解析或默认分数将添加到评分列表中。这部分系统充当检索评价器或评分器。
retrieve_from_vectorstore 函数实现了 vectorstore.similarity_search ,基于查询找到 8 个最相关的文档分块,并从这些检索到的 LangChain 文档对象中检索 page_content 。
本步骤构建了 corrective RAG 系统的概念框架,使 LLM 能够评估上下文质量,并从内部与外部知识源中检索信息。
初始检索 功能用于扫描 PDF 的向量存储。
上下文评分 根据相关性对已检索到的 PDF 分块进行上下文评分。
Tavily 回退机制: 当 PDF 没有足够的相关上下文,就会启动 Tavily(网络搜索)。
来源验证 是一个由 LLM 驱动的步骤,用于在使用 Tavily 结果之前检查它们是否与私有保单相关。此功能可避免公共健康计划等无关信息的干扰。
查询重写和第二次 Tavily 搜索 :如果仍无有效上下文,则重写查询并再次尝试 Tavily 搜索。
最终决策:当有任何相关上下文时,就会将其发送给 LLM,结合严格限制的提示生成答案。如果在所有可行的尝试后都没有相关的上下文,则返回礼貌的拒绝响应。
policy_context_keywords 参数的第一次传递允许您添加保单中的特定术语(例如保单名称、保险公司),以帮助缩小 Tavily 的搜索范围。
MIN_CONTEXT_LENGTH 定义检索上下文的最小可接受长度。
SIMILARITY_THRESHOLD 定义文本分块被视为“优质”所需的最低相关性分数。
def corrective_rag(...) 定义协调整个纠正性 RAG 工作流的主函数。
corrective_rag 函数首先创建 retrieved_context_pieces 来收集相关上下文。它首先根据查询从 PDF 向量存储中获取 chunks_from_vectorstore 并对其进行评分,然后 scored_chunks_vector 使用语言模型评估其相关性。仅保留符合 SIMILARITY_THRESHOLD 的 good_chunks_vector 。然后根据这些片段编译 current_context 。
如果current_context低于MIN_CONTEXT_LENGTH,系统将尝试网络搜索。它构建 tavily_search_query,其中可能包含 policy_context_keywords。执行直接搜索(tavily_context_direct)。至关重要的是,创建 verify_prompt 并将其发送给 LLM,用于判断网络搜索结果 ( is_relevant_source) 是否来自私有保单而非公共计划。如果答案是“是”,则添加上下文。
如果上下文仍然不足,系统将准备重写查询。它使用 rewrite_prompt 从 LLM 获取 improved_query ,然后执行第二次网络搜索(tavily_context_rewritten)。新上下文同样需经过来源验证。
最后, if len(current_context.strip()) == 0 执行最终检查。如果在所有尝试后仍未找到相关上下文,则返回预定义的拒绝消息。否则,使用所有已验证上下文创建 final_prompt ,并发送至语言模型生成最终答案。
整个 corrective_rag 函数详细实现了纠正性 RAG 的分阶段检索、评分与验证功能。它允许不断更新知识库和知识流,并能提供兼具稳健性与上下文感知能力的响应。
最后,使用示例查询执行 corrective_rag 函数。关键是要提供与您的 PDF 文档密切相关的 policy_context_keywords 。这些关键词将帮助 Tavily 网络搜索更精准地匹配实际保单内容,防止通用信息或公共健康计划信息污染上下文环境。
观察打印输出的上下文长度与验证结果,了解信息流转过程。
policy_specific_keywords = ["Super Star Health", "Care Health Insurance"] 定义了与上传保单相关的关键词列表,用于精确限定网络搜索结果范围。
query = "..." 定义用户可能提出的特定问题。
result = corrective_rag(query, policy_context_keywords=policy_specific_keywords) 调用主 corrective_rag 函数,传递用户查询及保单特定关键词以启动完整 RAG 流程。
print("\n FINAL ANSWER (...)") 在输出生成答案前显示清晰标题。
print(result) 输出 corrective_rag 系统返回的最终答案。
本步骤展示了如何通过示例查询与关键词调用完整纠正性 RAG 系统,在实际场景中演示其端到端运行机制。
已实现的纠正性 RAG 完整协调内部 PDF 知识库与外部服务,为复杂请求检索全面信息。
它通过基于 LLM 的评分机制与关键来源验证,精准评估并过滤检索到的上下文,确保使用有效可靠的信息。
该系统通过智能重写用户查询以获取更精准、更高质量的信息,展现了优化外部搜索的能力。
通过使用受限生成技术,系统能稳定生成上下文准确的答案,如果没有足够的已知验证信息,系统会礼貌地拒绝回答。
此示例演示了如何使用 LangChain 和 watsonx 平台上的 IBM Granite LLM,在保单问题等敏感领域开发强大且值得信赖的基于 AI 的应用程序。
构建、部署和管理强大的 AI 助手和智能体,运用生成式 AI 实现工作流和流程自动化。
借助值得信赖的 AI 解决方案,您可以勾勒未来业务发展蓝图。
IBM Consulting AI 服务有助于重塑企业利用 AI 实现转型的方式。