【LangChain】对话式问答(Conversational Retrieval QA)

LangChain学习文档

  • 流行
    • 【LangChain】Retrieval QA
    • 【LangChain】对话式问答(Conversational Retrieval QA)

概要

本篇详细介绍:对话式问答 的内容有:

  1. 对话式问答chain是建立在问答chain的基础之上,提供了聊天历史记录的功能,对话式问答主要由ConversationBufferMemory类构建。
  2. 聊天记录我们可以使用memory来记录;在ConversationalRetrievalChain.from_llm()方法中指定;也可以在执行时候指定:qa({"question": query, "chat_history": chat_history})
  3. 针对问题答案,可以使用不同的模型;用便宜的模型来提炼问题,用昂贵的模型回答问题;提炼问题是在ConversationalRetrievalChain.from_llm()方法中指定参数:condense_question_llm
  4. 如果向量存储支持距离搜索,可以使用vectordbkwargs来指定阈值。
  5. 可以将不同类型的文档链与 ConversationalRetrievalChain 链结合使用。
  6. 想要获取历史记录,可以使用get_chat_history函数;

内容

ConversationalRetrievalQA chain 是建立在 RetrievalQAChain 之上,提供聊天历史记录的组件。
它首先将聊天记录(显式传入或从提供的内存中检索)和问题组合成一个独立的问题,然后从检索器中查找相关文档,最后将这些文档和问题传递到问答链以返回一个响应。

想要创建一个,那我们就需要retriever。在下面的示例中,我们将从向量存储创建一个,该向量存储可以从embeddings中创建。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.llms import OpenAI
from langchain.chains import ConversationalRetrievalChain

# 加载文档。您可以将其替换为您想要的任何类型数据的加载器:
from langchain.document_loaders import TextLoader
loader = TextLoader("../../state_of_the_union.txt")
documents = loader.load()

# 如果您想要组合多个加载器,您可以执行以下操作:
# loaders = [....]
# docs = []
# for loader in loaders:
#     docs.extend(loader.load())
# 现在,我们分割文档,为它们创建嵌入,并将它们放入矢量存储中。这使我们能够对它们进行语义搜索。
# 创建拆分器
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
# 拆分文档
documents = text_splitter.split_documents(documents)

# 创建embeddings
embeddings = OpenAIEmbeddings()
# 创建向量存储,使得我们可以进行相关性搜索
vectorstore = Chroma.from_documents(documents, embeddings)

使用没有持久性的嵌入式 DuckDB:数据将是暂时的

为了追踪输入、输出,我们需要创建一个memory对象。

from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

我们现在初始化 ConversationalRetrievalChain

qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectorstore.as_retriever(), memory=memory)

query = "总统对科坦吉·布朗·杰克逊说了些什么"
result = qa({"question": query})
# 结果
""" 
总统表示,科坦吉·布朗·杰克逊是美国顶尖的法律专家之一、前私人执业顶级诉讼律师、前联邦公设辩护律师,出身于公立学校教育工作者和警察家庭。
他还表示,她是共识的缔造者,得到了从警察兄弟会到民主党和共和党任命的前法官的广泛支持。
"""

query = "他有没有提到她继承了谁"
result = qa({"question": query})

result['answer']

""" 
科坦吉·布朗·杰克逊 (Ketanji Brown Jackson) 接替斯蒂芬·布雷耶 (Stephen Breyer) 大法官出任美国最高法院法官。
"""

小节下:我们创建了一个拥有历史记录的聊天会话

在聊天历史中传递(Pass in chat history)

在上面的示例中,我们使用 Memory 对象来追踪聊天历史记录。
我们也可以直接将其显式传递。 为了做到这一点,我们需要初始化一个没有任何历史记录的链。

qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectorstore.as_retriever())
# 这是在没有聊天记录的情况下提问的示例
chat_history = []
query = "总统对科坦吉·布朗·杰克逊说了些什么"
result = qa({"question": query, "chat_history": chat_history})
result["answer"]

# 结果
""" 
总统表示,科坦吉·布朗·杰克逊是美国顶尖的法律专家之一、前私人执业顶级诉讼律师、前联邦公设辩护律师,出身于公立学校教育工作者和警察家庭。
他还表示,她是共识的缔造者,得到了从警察兄弟会到民主党和共和党任命的前法官的广泛支持。
"""

# 这是一个使用一些聊天记录提问的示例
chat_history = [(query, result["answer"])]
query = "他有没有提到她继承了谁"
result = qa({"question": query, "chat_history": chat_history})
result['answer']
# 结果
""" 科坦吉·布朗·杰克逊 (Ketanji Brown Jackson) 接替斯蒂芬·布雷耶 (Stephen Breyer) 大法官出任美国最高法院法官 """

使用不同的模型来凝练问题(Using a different model for condensing the question)

该链有两个步骤:
首先,它将当前问题和聊天历史凝练为一个独立的问题。这是创建用于检索的独立向量所必需的。

之后,它进行检索,然后使用单独模型的检索增强生成来回答问题。 LangChain 声明性特性的部分强大之处在于,您可以轻松地为每个调用使用单独的语言模型。

这样会有一个非常大的好处:我们可以使用更便宜、更快的模型来完成问题的凝练工作,然后再使用昂贵的模型来回答问题。

from langchain.chat_models import ChatOpenAI
# 构建一个组合
qa = ConversationalRetrievalChain.from_llm(
    ChatOpenAI(temperature=0, model="gpt-4"),
    vectorstore.as_retriever(),
    condense_question_llm = ChatOpenAI(temperature=0, model='gpt-3.5-turbo'),
)

chat_history = []
query = "总统对科坦吉·布朗·杰克逊说了些什么"
result = qa({"question": query, "chat_history": chat_history})

chat_history = [(query, result["answer"])]
query = "他有没有提到她继承了谁"
result = qa({"question": query, "chat_history": chat_history})

返回源文件(Return Source Documents)

您还可以轻松地从 ConversationalRetrievalChain 返回源文档。当您想要检查返回的文档时,这非常有用。

qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectorstore.as_retriever(), return_source_documents=True)

result['source_documents'][0]
    Document(page_content='Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections. \n\nTonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service. \n\nOne of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n\nAnd I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.', metadata={'source': '../../state_of_the_union.txt'})

带有 search_distance 的 ConversationalRetrievalChain

如果您使用的向量存储支持按搜索距离来过滤,则可以添加阈值参数。

# 这里设置了阈值
vectordbkwargs = {"search_distance": 0.9}

qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectorstore.as_retriever(), return_source_documents=True)
chat_history = []
query = "What did the president say about Ketanji Brown Jackson"
result = qa({"question": query, "chat_history": chat_history, "vectordbkwargs": vectordbkwargs})

带有map_reduce的ConversationalRetrievalChain

我们还可以将不同类型的文档链与 ConversationalRetrievalChain 链结合使用。

from langchain.chains.qa_with_sources import load_qa_with_sources_chain
llm = OpenAI(temperature=0)
question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT)
doc_chain = load_qa_with_sources_chain(llm, chain_type="map_reduce")

chain = ConversationalRetrievalChain(
    retriever=vectorstore.as_retriever(),
    question_generator=question_generator,
    combine_docs_chain=doc_chain,
)

chat_history = []
query = "What did the president say about Ketanji Brown Jackson"
result = chain({"question": query, "chat_history": chat_history})
result['answer']
""" 
The president said that Ketanji Brown Jackson is one of the nation's top legal minds, a former top litigator in private practice, a former federal public defender, from a family of public school educators and police officers, a consensus builder, and has received a broad range of support from the Fraternal Order of Police to former judges appointed by Democrats and Republicans. 
\nSOURCES: ../../state_of_the_union.txt
"""

ConversationalRetrievalChain 流式传输到标准输出

在此示例中,chain的输出将按token流式传输到标准输出token。

from langchain.chains.llm import LLMChain
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.chains.conversational_retrieval.prompts import CONDENSE_QUESTION_PROMPT, QA_PROMPT
from langchain.chains.question_answering import load_qa_chain

# 使用流式 llm 构造一个 ConversationalRetrievalChain 来合并文档
# 以及一个用于问题生成的独立的非流式 llm
llm = OpenAI(temperature=0)
streaming_llm = OpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler()], temperature=0)

question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT)
doc_chain = load_qa_chain(streaming_llm, chain_type="stuff", prompt=QA_PROMPT)

qa = ConversationalRetrievalChain(
    retriever=vectorstore.as_retriever(), combine_docs_chain=doc_chain, question_generator=question_generator)

chat_history = []
query = "总统对科坦吉·布朗·杰克逊说了些什么"
result = qa({"question": query, "chat_history": chat_history})
# 结果
""" 
总统表示,科坦吉·布朗·杰克逊是美国顶尖的法律专家之一、前私人执业顶级诉讼律师、前联邦公设辩护律师,出身于公立学校教育工作者和警察家庭。
他还表示,她是共识的缔造者,得到了从警察兄弟会到民主党和共和党任命的前法官的广泛支持。
"""

chat_history = [(query, result["answer"])]
query = "Did he mention who she suceeded"
result = qa({"question": query, "chat_history": chat_history})
# 结果
"""
科坦吉·布朗·杰克逊 (Ketanji Brown Jackson) 接替斯蒂芬·布雷耶 (Stephen Breyer) 大法官出任美国最高法院法官。
"""

get_chat_history 函数

您还可以指定 get_chat_history 函数,该函数可用于格式化 chat_history 字符串。

def get_chat_history(inputs) -> str:
    res = []
    for human, ai in inputs:
        res.append(f"Human:{human}\nAI:{ai}")
    return "\n".join(res)
qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectorstore.as_retriever(), get_chat_history=get_chat_history)

chat_history = []
query = "What did the president say about Ketanji Brown Jackson"
result = qa({"question": query, "chat_history": chat_history})

result['answer']
""" 
总统表示,科坦吉·布朗·杰克逊是美国顶尖的法律专家之一、前私人执业顶级诉讼律师、前联邦公设辩护律师,出身于公立学校教育工作者和警察家庭。
他还表示,她是共识的缔造者,得到了从警察兄弟会到民主党和共和党任命的前法官的广泛支持。
"""

参考地址:

Conversational Retrieval QA

你可能感兴趣的:(LangChain,AI,langchain)