使用LangChain和LangGraph大幅提升RAG效果

一、简介

LangGraph是LangChain、LangServe和LangSmith系列的最新成员,旨在使用LLM构建生成式人工智能应用程序。请记住,所有这些都是独立的包,必须单独进行pip安装。

在深入学习LangGraph之前,需要了解LangChain的两个主要概念。

1. 链:围绕LLM编写的程序,用于执行任务,例如自动SQL编写或NER提取链等。请注意,链不能用于任何其他任务(甚至不能用于一般用例),如果尝试这样做,可能会损坏链。链中要遵循的步骤是预定义的,不可灵活调整。

2. 代理:链的更加灵活版本,代理通常是启用第三方工具(例如谷歌搜索、YouTube)的LLM,由LLM本身决定下一步如何解决给定的查询。

现在,当处理现实世界的问题时,一个常见的问题是希望找到介于链和代理之间的解决方案。即不像链那样硬编码,但也不像代理那样完全由LLM驱动。

二、LangGraph

LangGraph是以LangChain为核心,用于创建工作流程中的循环图的工具。因此,我们假设以下示例:

你希望在知识库上搭建一个基于RAG的检索系统。现在,你希望引入这样一种情况:如果RAG的输出未满足特定质量要求,代理/链应该再次检索数据,但这次是自行更改提示。并且重复此过程,直到达到质量阈值为止。

使用LangGraph可以实现这种循环逻辑。这只是一个示例,使用LangGraph还可以做更多事情。

注:可以将其视为向链中引入循环逻辑,使其成为循环链。

  • LangGraph对于构建Autogen或MetaGPT等多代理应用程序至关重要。

顾名思义,LangGraph具有一般图形所具有的所有组件,例如节点、边等,接下来通过一个示例来了解。

三、使用LangGraph改善RAG

在此示例中,希望将RAG系统在数据库中的最终输出减少到不超过30个字符。如果输出长度大于30个字符,则希望引入循环,使用不同的提示再次尝试,直到长度小于30个字符为止。这是一个演示目的的基本逻辑。你甚至可以实现复杂的逻辑来改善RAG结果。

我们将创建的图形如下所示。

图片图片

此处使用的版本为 langchain===0.0.349, openai===1.3.8, langgraph===0.0.26。

3.1 首先,让我们导入重要的内容并初始化LLM。这里使用的是OpenAI API,但你也可以使用其他LLM。

from typing import Dict, TypedDict, Optional
from langgraph.graph import StateGraph, END
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
from langchain.embeddings.openai import OpenAIEmbeddings

llm = OpenAI(openai_api_key='your API')

接下来,我们将定义一个StateGraph。

class GraphState(TypedDict):
    question: Optional[str] = None
    classification: Optional[str] = None
    response: Optional[str] = None
    length: Optional[int] = None
    greeting: Optional[str] = None

workflow = StateGraph(GraphState)

什么是StateGraph?

StateGraph是任何LangGraph流程的核心,它存储了在执行工作流程时我们将存储的各种变量的状态。在本例中,我们有5个变量,其值在执行图形时将进行更新,并将与所有边和节点共享。

3.2 接下来,让我们从现有向量数据库中初始化一个RAG检索链。代码已在以下视频中进行了解释。

def retriever_qa_creation():
        embeddings = OpenAIEmbeddings()
        db = Chroma(embedding_functinotallow=embeddings,persist_directory='/database',collection_name='details')
        qa = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=db.as_retriever())
        return qa

rag_chain = retriever_qa_creation()

3.3 接下来,我们将向该图形添加节点。

def classify(question):
    return llm("classify intent of given input as greeting or not_greeting. Output just the class.Input:{}".format(question)).strip()

def classify_input_node(state):
    question = state.get('question', '').strip()
    classification = classify(question) 
    return {"classification": classification}

def handle_greeting_node(state):
    return {"greeting": "Hello! How can I help you today?"}

def handle_RAG(state):
    question = state.get('question', '').strip()
    prompt = question
    if state.get("length")30,使用第二个提示符。
<p>由于第二个条件边界,移至bye。</p>
<p>END。</p>
<p>如果没有使用LangGraph:</p>
<pre><code>rag_chain.run("Mehul developed which projects?")

1. 输出
"Mehul developed projects like ABC, XYZ, QWERTY. Not only these, he has major contribution in many other projects as well at OOO organization"

3.7 下一个输入。

app.invoke({'question':'Hello bot','length':0})

1. 输出
{'question': 'Hello bot',
 'classification': 'greeting',
 'response': None,
 'length': 0,
 'greeting': 'Hello! How can I help you today?'}

这里的流程会更简单。

classify_input: 情感将为greeting。

由于第一个条件边界,移至handle_greeting。

END。

虽然我在这里应用的条件相当简单,但通过添加更复杂的条件,这个框架可以很容易地用于改进你的结果。