在 crag 中用 LangGraph 进行评分知识精炼-下

在上一次给大家展示了基本的 Rag 检索过程,着重描述了增强检索中的知识精炼和补充检索,这些都是 crag 的一部分,这篇内容结合 langgraph 给大家展示通过检索增强生成(Retrieval-Augmented Generation, RAG)的工作流,来处理问题并生成答案。
好了,下面我们直接开始代码。

定义graph

首先我们还是先定义 web 搜索工具,这里的 web 搜索工具作为补充检索很重要:

### Search
from langchain_community.tools.tavily_search import TavilySearchResults

web_search_tool = TavilySearchResults(k=3)

然后定义我们 graph 的基本状态结构:

from typing import List
from typing_extensions import TypedDict
class GraphState(TypedDict):
    """
    表示我们图的状态。

    属性:
        question: 问题
        generation: 大语言模型生成的内容
        web_search: 是否使用web搜索
        documents: 检索出来的文档列表
    """
    question: str
    generation: str
    web_search: str
    documents: List[str]

定义工作流节点

定义检索与问题相关的文档的节点:

def retrieve(state):
    """
    检索文档

    参数:
        state (dict): 当前图状态

    返回:
        state (dict): 更新后的状态,包含检索到的文档
    """
    print("---RETRIEVE---")
    question = state["question"]

    # 檢索相关文档
    documents = retriever.get_relevant_documents(question)
    return {"documents": documents, "question": question}

定义基于检索到的文档生成答案的节点:

def generate(state):
    """
    生成答案

    参数:
        state (dict): 当前图状态

    返回:
        state (dict): 更新后的状态,包含生成的答案
    """
    print("---GENERATE---")
    question = state["question"]
    documents = state["documents"]

    # RAG generation
    generation = rag_chain.invoke({"context": documents, "question": question})
    return {"documents": documents, "question": question, "generation": generation}

定义评估检索到的文档是否与问题相关的节点:

def grade_documents(state):
    """
    评估检索到的文档是否与问题相关

    参数:
        state (dict): 当前图状态

    返回:
        state (dict): 更新后的状态,仅包含相关文档
    """

    print("---CHECK DOCUMENT RELEVANCE TO QUESTION---")
    question = state["question"]
    documents = state["documents"]

    # Score each doc
    filtered_docs = []
    web_search = "No"
    for d in documents:
        score = retrieval_grader.invoke(
            {"question": question, "document": d.page_content}
        )
        grade = score.binary_score
        if grade == "yes":
            print("---GRADE: DOCUMENT RELEVANT---")
            filtered_docs.append(d)
        else:
            print("---GRADE: DOCUMENT NOT RELEVANT---")
            web_search = "Yes"
            continue
    return {"documents": filtered_docs, "question": question, "web_search": web_search}

重写问题以生成更好的查询:

def transform_query(state):
    """
    重写问题以生成更好的查询

    参数:
        state (dict): 当前图状态

    返回:
        state (dict): 更新后的状态,包含重写后的问题
    """

    print("---TRANSFORM QUERY---")
    question = state["question"]
    documents = state["documents"]

    # Re-write question
    better_question = question_rewriter.invoke({"question": question})
    return {"documents": documents, "question": better_question}

基于重写后的问题进行网络搜索,并将结果添加到文档列表中。

def web_search(state):
    """
    基于重写后的问题进行网络搜索

    参数:
        state (dict): 当前图状态

    返回:
        state (dict): 更新后的状态,包含网络搜索结果
    """

    print("---WEB SEARCH---")
    question = state["question"]
    documents = state["documents"]

    # Web search
    docs = web_search_tool.invoke({"query": question})
    web_results = "\n".join([d["content"] for d in docs])
    web_results = Document(page_content=web_results)
    documents.append(web_results)

    return {"documents": documents, "question": question}

最后定义我们的条件边,决定是生成答案还是重写问题。

def decide_to_generate(state):
    """
    决定是生成答案还是重写问题

    参数:
        state (dict): 当前图状态

    返回:
        str: 下一个节点的决策("transform_query" 或 "generate")
    """

    print("---ASSESS GRADED DOCUMENTS---")
    # state["question"]
    web_search = state["web_search"]
    # state["documents"]

    if web_search == "Yes":
        # All documents have been filtered check_relevance
        # We will re-generate a new query
        print(
            "---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---"
        )
        return "transform_query"
    else:
        # We have relevant documents, so generate answer
        print("---DECISION: GENERATE---")
        return "generate"

这里我们稍微等一下,做个小总结,上面定义的一系列的工作流节点主要是下面几个用途:

1.检索文档: 调用 retrieve 函数,获取与问题相关的文档。
2:评估文档相关性: 调用 grade_documents 函数,过滤掉不相关的文档。
3:决定下一步:如果所有文档都不相关,调用 transform_query 重写问题,然后进行网络搜索(web_search)。如果有相关文档,调用 generate 生成答案。生成答案: 调用 generate 函数,基于相关文档生成最终答案。

创建工作流

我们把上面定义好的节点和边组织在一起:

from langgraph.graph import END, StateGraph, START

# 创建工作流
workflow = StateGraph(GraphState)

# 定义节点
workflow.add_node("retrieve", retrieve)  # 检索文档
workflow.add_node("grade_documents", grade_documents)  # 评估文档相关性
workflow.add_node("generate", generate)  # 生成答案
workflow.add_node("transform_query", transform_query)  # 重写问题
workflow.add_node("web_search_node", web_search)  # 网络搜索

# 构建工作流
workflow.add_edge(START, "retrieve")  # 从 START 到 retrieve
workflow.add_edge("retrieve", "grade_documents")  # 从 retrieve 到 grade_documents
workflow.add_conditional_edges(
    "grade_documents",  # 从 grade_documents 出发
    decide_to_generate,  # 根据 decide_to_generate 的返回值决定下一步
    {
        "transform_query": "transform_query",  # 如果返回 "transform_query",跳转到 transform_query 节点
        "generate": "generate",  # 如果返回 "generate",跳转到 generate 节点
    },
)
workflow.add_edge("transform_query", "web_search_node")  # 从 transform_query 到 web_search_node
workflow.add_edge("web_search_node", "generate")  # 从 web_search_node 到 generate
workflow.add_edge("generate", END)  # 从 generate 到 END

# 编译工作流
app = workflow.compile()

from IPython.display import display, Image
display(Image(graph.get_graph().draw_mermaid_png()))

得到下面的图形化结果:
在这里插入图片描述
我们这里就完成了整个工作流的逻辑框架,下面我们来调用这个 graph 试一下,看能得出什么结果:

from pprint import pprint

inputs = {"question": "agent memory 的类型有哪些?"}
for output in graph.stream(inputs):
    for key, value in output.items():
        # Node
        pprint(f"Node '{key}':")
        pprint(f"value '{value}':")

    pprint("\n---\n")

在这里插入图片描述
我们分析执行流程日志就可以发现,我们采用一个基于状态的工作流 (StateGraph),通过不同的节点和边来处理问题并生成答案的方式串联起了我们 crag 的整个流程,然后可以看到它是怎么来进行检索,然后怎么来调用工具的,到最后怎么完成知识精炼和补充,然后大模型返回给我们增强后的答案。大家可以根据上面的代码自己试一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/963452.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5.3 C++ CDO的初步理解

一.UObject UObject是所有对象的基类,往上还有UObjectBaseUtility。 注释:所有虚幻引擎对象的基类。对象的类型由基于 UClass 类来定义。 这为创建和使用UObject的对象提供了 函数,并且提供了应在子类中重写的虚函数。 /** * The base cla…

知识库管理在提升企业决策效率与知识共享中的应用探讨

内容概要 知识库管理是指企业对内部知识、信息进行系统化整理和管理的过程,其重要性在于为企业决策提供了坚实的数据支持与参考依据。知识库管理不仅能够提高信息的获取速度,还能有效减少重复劳动,提升工作效率。在如今快速变化的商业环境中…

Linux:线程池和单例模式

一、普通线程池 1.1 线程池概念 线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价&…

AJAX笔记原理篇

黑马程序员视频地址: AJAX-Day03-01.XMLHttpRequest_基本使用https://www.bilibili.com/video/BV1MN411y7pw?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p33https://www.bilibili.com/video/BV1MN411y7pw?vd_sour…

ComfyUI安装调用DeepSeek——DeepSeek多模态之图形模型安装问题解决(ComfyUI-Janus-Pro)

ComfyUI 的 Janus-Pro 节点,一个统一的多模态理解和生成框架。 试用: https://huggingface.co/spaces/deepseek-ai/Janus-1.3B https://huggingface.co/spaces/deepseek-ai/Janus-Pro-7B https://huggingface.co/spaces/deepseek-ai/JanusFlow-1.3B 安装…

3D图形学与可视化大屏:什么是材质属性,有什么作用?

一、颜色属性 漫反射颜色 漫反射颜色决定了物体表面对入射光进行漫反射后的颜色。当光线照射到物体表面时,一部分光被均匀地向各个方向散射,形成漫反射。漫反射颜色的选择会直接影响物体在光照下的外观。例如,一个红色的漫反射颜色会使物体在…

JVM方法区

一、栈、堆、方法区的交互关系 二、方法区的理解: 尽管所有的方法区在逻辑上属于堆的一部分,但是一些简单的实现可能不会去进行垃圾收集或者进行压缩,方法区可以看作是一块独立于Java堆的内存空间。 方法区(Method Area)与Java堆一样,是各个…

租赁管理系统在促进智能物业运营中的关键作用和优化策略分析

租赁管理系统在智能物业运营中的关键作用与优化策略 随着科技的飞速发展,租赁管理系统在智能物业运营中扮演着越来越重要的角色。这种系统不仅提高了物业管理的效率,更是促进了资源的优化配置和客户关系的加强。对于工业园、产业园、物流园、写字楼和公…

LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略

LLMs之DeepSeek:Math-To-Manim的简介(包括DeepSeek R1-Zero的详解)、安装和使用方法、案例应用之详细攻略 目录 Math-To-Manim的简介 1、特点 2、一个空间推理测试—考察不同大型语言模型如何解释和可视化空间关系 3、DeepSeek R1-Zero的简介:处理更…

【课题推荐】基于t分布的非高斯滤波框架在水下自主导航中的应用研究

水下自主导航系统在海洋探测、环境监测及水下作业等领域具有广泛的应用。然而,复杂的水下环境常常导致传感器输出出现野值噪声,这些噪声会严重影响导航信息融合算法的精度,甚至导致系统发散。传统的卡尔曼滤波算法基于高斯噪声假设&#xff0…

Arduino大师练成手册 -- 控制 AS608 指纹识别模块

要在 Arduino 上控制 AS608 指纹识别模块,你可以按照以下步骤进行: 硬件连接 连接指纹模块:将 AS608 指纹模块与 Arduino 连接。通常,AS608 使用 UART 接口进行通信。你需要将 AS608 的 TX、RX、VCC 和 GND 引脚分别连接到 Ardu…

深度学习深度解析:从基础到前沿

引言 深度学习作为人工智能的一个重要分支,通过模拟人脑的神经网络结构来进行数据分析和模式识别。它在图像识别、自然语言处理、语音识别等领域取得了显著成果。本文将深入探讨深度学习的基础知识、主要模型架构以及当前的研究热点和发展趋势。 基础概念与数学原理…

享元模式——C++实现

目录 1. 享元模式简介 2. 代码示例 1. 享元模式简介 享元模式是一种结构型模式。 享元模式用于缓存共享对象,降低内存消耗。共享对象相同的部分,避免创建大量相同的对象,减少内存占用。 享元模式需要将对象分成内部状态和外部状态两个部分…

ResNet--深度学习中的革命性网络架构

一、引言 在深度学习的研究和应用中,网络架构的设计始终是一个关键话题。随着计算能力和大数据的不断提升,深度神经网络逐渐成为解决复杂任务的主流方法。然而,随着网络层数的增加,训练深度神经网络往往面临梯度消失或梯度爆炸的…

HTML特殊符号的使用示例

目录 一、基本特殊符号的使用 1、空格符号: 2、小于号 和 大于号: 3、引号: 二、版权、注册商标符号的使用 1、版权符号:© 2、注册商标符号: 三、数学符号的使用 四、箭头符号的使用 五、货币符号的使用…

C++11—右值引用

目录 简介 左值和右值 左值 右值 右值引用 生命周期 引用折叠 实际应用 移动语义 移动构造函数 移动赋值运算符 完美转发 简介 之前我们曾学习过引用叫左值引用,但那是C98的,在C11中新增了一种引用叫右值引用。右值引用主要用于支持移动语…

Ubuntu下的Doxygen+VScode实现C/C++接口文档自动生成

Ubuntu下的DoxygenVScode实现C/C接口文档自动生成 Chapter1 Ubuntu下的DoxygenVScode实现C/C接口文档自动生成1、 Doxygen简介1. 安装Doxygen1)方法一:2)方法二:2. doxygen注释自动生成插件3. doxygen注释基本语法4. doxygen的生成…

函数与递归

函数与递归 声明或者定义应该在使用之前(不单单针对于函数) 函数对全局变量做出的改变还是不会随着函数结束而消失的 函数声明在main函数里面也是可以的 引用变量和引用实体的变化是一样的 传址调用比传值调用效率高 重载函数->编译器会根据传递…

网络编程套接字(中)

文章目录 🍏简单的TCP网络程序服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求客户端创建套接字客户端连接服务器客户端发起请求服务器测试单执行流服务器的弊端 🍐多进程版的TCP网络程序捕捉SIGCHLD信号让孙子进程提供服务 &#x1…

96,【4】 buuctf web [BJDCTF2020]EzPHP

进入靶场 查看源代码 GFXEIM3YFZYGQ4A 一看就是编码后的 1nD3x.php 访问 得到源代码 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;用于调试或展示代码结构 highlight_file(__FILE__); // 关闭所有 PHP 错误报告&#xff0c;防止错误信息泄露可能的安全漏洞 erro…