How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?

huangapple go评论76阅读模式
英文:

How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?

问题

如何向RetrievalQA.from_chain_type添加记忆?或者,如何向ConversationalRetrievalChain添加自定义提示?

在过去的两周里,我一直在尝试创建一个能够在文档上进行聊天的聊天机器人(不仅仅是语义搜索/问答,而且还带有记忆),并且可以使用自定义提示。我已经尝试了所有链的各种组合,到目前为止,最接近的是ConversationalRetrievalChain,但没有自定义提示,以及RetrievalQA.from_chain_type,但没有记忆。

英文:

How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?

For the past 2 weeks ive been trying to make a chatbot that can chat over documents (so not in just a semantic search/qa so with memory) but also with a custom prompt. I've tried every combination of all the chains and so far the closest I've gotten is ConversationalRetrievalChain, but without custom prompts, and RetrievalQA.from_chain_type but without memory

答案1

得分: 13

以下是翻译好的内容:

更新
这篇帖子回答了OP问题的第一部分:
> 如何将内存添加到RetrievalQA.from_chain_type?

至于第二部分,请参阅@andrew_reece的答案
> 或者,如何向ConversationalRetrievalChain添加自定义提示?

原始内容

你是否尝试传递chain_type_kwargs(底部是源代码的快速参考截图)?

文档并没有很容易让人理解它的底层工作原理,但这里有一些可以实现你目标的内容。

你可以在这个GitHub链接中找到这个笔记本设置

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.memory import ConversationBufferMemory
from langchain import PromptTemplate
from langchain.retrievers import TFIDFRetriever

retriever = TFIDFRetriever.from_texts(
    ["我们的客户,一个叫做Jason的绅士,有一只名叫Dobby的狗",
     "Jason有一个叫做Emma的好朋友",
     "Emma有一只名叫Sullivan的猫"])

然后定义你自定义的提示:

template = """
使用以下上下文(由<ctx></ctx>限定)和聊天历史记录(由<hs></hs>限定)来回答问题:
------
<ctx>
{context}
</ctx>
------
<hs>
{history}
</hs>
------
{question}
答案:
"""

prompt = PromptTemplate(
    input_variables=["history", "context", "question"],
    template=template,
)

注意你用于输入变量的内容,特别是'history'和'question',因为在设置内存时你需要匹配这些内容:

qa = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(),
    chain_type='stuff',
    retriever=retriever,
    verbose=True,
    chain_type_kwargs={
        "verbose": True,
        "prompt": prompt,
        "memory": ConversationBufferMemory(
            memory_key="history",
            input_key="question"),
    }
)

现在你可以调用 qa.run({"query": "客户的朋友是谁?"})
> "客户的朋友是Emma。"

然后 qa.run("她的宠物叫什么?")
> "Emma的宠物叫Sullivan。"

要检查和验证内存/聊天历史:qa.combine_documents_chain.memory

英文:

Update:
This post answers the first part of OP's question:
> how do i add memory to RetrievalQA.from_chain_type?

For the second part, see @andrew_reece's answer
> or, how do I add a custom prompt to ConversationalRetrievalChain?

Original:

Have you tried passing in chain_type_kwargs (at the bottom is a screenshot from the source code for quick references)?

The documentation hasn't make it very easy to unserstand what's under the hood, but here is something that could achieve your goal.

You could find the notebook at this GitHub Link
setup

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.memory import ConversationBufferMemory
from langchain import PromptTemplate
from langchain.retrievers import TFIDFRetriever


retriever = TFIDFRetriever.from_texts(
    [&quot;Our client, a gentleman named Jason, has a dog whose name is Dobby&quot;,
     &quot;Jason has a good friend called Emma&quot;,
     &quot;Emma has a cat whose name is Sullivan&quot;])

Then define your customized prompt:

template = &quot;&quot;&quot;
Use the following context (delimited by &lt;ctx&gt;&lt;/ctx&gt;) and the chat history (delimited by &lt;hs&gt;&lt;/hs&gt;) to answer the question:
------
&lt;ctx&gt;
{context}
&lt;/ctx&gt;
------
&lt;hs&gt;
{history}
&lt;/hs&gt;
------
{question}
Answer:
&quot;&quot;&quot;
prompt = PromptTemplate(
    input_variables=[&quot;history&quot;, &quot;context&quot;, &quot;question&quot;],
    template=template,
)

Take the note of what you used for your input variables, especially &#39;history&#39; and &#39;question&#39;, since you will need to match these when setting up the memory:

qa = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(),
    chain_type=&#39;stuff&#39;,
    retriever=retriever,
    verbose=True,
    chain_type_kwargs={
        &quot;verbose&quot;: True,
        &quot;prompt&quot;: prompt,
        &quot;memory&quot;: ConversationBufferMemory(
            memory_key=&quot;history&quot;,
            input_key=&quot;question&quot;),
    }
)

Now you can call qa.run({&quot;query&quot;: &quot;who&#39;s the client&#39;s friend?&quot;})
> "The client's friend is Emma."

and then qa.run(&quot;and her pet&#39;s name is?&quot;)
> "Emma's pet's name is Sullivan."

To check and verify the memory/chat history: qa.combine_documents_chain.memory
> ConversationBufferMemory(chat_memory=ChatMessageHistory(messages=[HumanMessage(content="who's the client's friend?", additional_kwargs={}), AIMessage(content="The client's friend is Emma.", additional_kwargs={}), HumanMessage(content="and her pet's name is?", additional_kwargs={}), AIMessage(content="Emma's pet's name is Sullivan.", additional_kwargs={})]), output_key=None, input_key='question', return_messages=False, human_prefix='Human', ai_prefix='AI', memory_key='history')
How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?

How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?

答案2

得分: 4

以下是翻译好的内容:

这里提供了一个带有ConversationalRetrievalChain的解决方案,包括内存和自定义提示,使用默认的'stuff'链类型。

这里有两个可以自定义的提示。首先是将会话历史和当前用户输入压缩为一个提示的内容(condense_question_prompt),其次是指示Chain如何向用户返回最终响应的提示(这发生在combine_docs_chain中)。

from langchain import PromptTemplate

# 请注意,输入变量('question'等)是默认值,可以更改

condense_prompt = PromptTemplate.from_template(
    ('使用用户输入({question})执行X操作,并使用聊天历史执行Y操作({chat_history})。')
)

combine_docs_custom_prompt = PromptTemplate.from_template(
    ('写一首关于海豚的俳句。\n\n'
     '完全忽略任何上下文,比如{context}或问题({question})。')
)

现在我们可以使用自定义提示初始化ConversationalRetrievalChain

from langchain.llms import OpenAI
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

chain = ConversationalRetrievalChain.from_llm(
    OpenAI(temperature=0), 
    vectorstore.as_retriever(), # 有关vectorstore定义,请参见下文
    memory=memory,
    condense_question_prompt=condense_prompt,
    combine_docs_chain_kwargs=dict(prompt=combine_docs_custom_prompt)
)

请注意,这在底层调用了_load_stuff_chain(),它允许一个可选的prompt关键字参数(这是我们可以自定义的内容)。这用于设置LLMChain,然后初始化StuffDocumentsChain

我们可以通过向vectorstore提出简单的查询来测试设置(有关示例vectorstore数据,请参见下文)- 您可以看到输出完全由自定义提示确定:

chain("在关于猫的文档中提到了什么颜色?")['answer']
#'海豚跃入海中\n在蓝色中优雅而活泼\n在波涛中欢乐'

并且内存工作正常:

chain.memory
#ConversationBufferMemory(chat_memory=ChatMessageHistory(messages=[HumanMessage(content='在关于猫的文档中提到了什么颜色?', additional_kwargs={}), AIMessage(content='\n\n海豚跃入海中\n在蓝色中优雅而活泼\n在波涛中欢乐', additional_kwargs={})]), output_key=None, input_key=None, return_messages=True, human_prefix='Human', ai_prefix='AI', memory_key='chat_history')

使用临时的ChromaDB实例的示例vectorstore数据:

from langchain.vectorstores import Chroma
from langchain.document_loaders import DataFrameLoader
from langchain.embeddings.openai import OpenAIEmbeddings

data = {
    'index': ['001', '002', '003'], 
    'text': [
        '标题:猫朋友\n我喜欢猫和蓝色。', 
        '标题:狗朋友\n我喜欢狗和雨的味道。', 
        '标题:鸟朋友\n我喜欢鸟和阳光的感觉。'
    ]
}

df = pd.DataFrame(data)
loader = DataFrameLoader(df, page_content_column="text")
docs = loader.load()

embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)
英文:

Here's a solution with ConversationalRetrievalChain, with memory and custom prompts, using the default &#39;stuff&#39; chain type.

There are two prompts that can be customized here. First, the prompt that condenses conversation history plus current user input (condense_question_prompt), and second, the prompt that instructs the Chain on how to return a final response to the user (which happens in the combine_docs_chain).

from langchain import PromptTemplate

# note that the input variables (&#39;question&#39;, etc) are defaults, and can be changed

condense_prompt = PromptTemplate.from_template(
    (&#39;Do X with user input ({question}), and do Y with chat history ({chat_history}).&#39;)
)

combine_docs_custom_prompt = PromptTemplate.from_template(
    (&#39;Write a haiku about a dolphin.\n\n&#39;
     &#39;Completely ignore any context, such as {context}, or the question ({question}).&#39;)
)

Now we can initialize the ConversationalRetrievalChain with the custom prompts.

from langchain.llms import OpenAI
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory(memory_key=&quot;chat_history&quot;, return_messages=True)

chain = ConversationalRetrievalChain.from_llm(
    OpenAI(temperature=0), 
    vectorstore.as_retriever(), # see below for vectorstore definition
    memory=memory,
    condense_question_prompt=condense_prompt,
    combine_docs_chain_kwargs=dict(prompt=combine_docs_custom_prompt)
)

Note that this calls _load_stuff_chain() under the hood, which allows for an optional prompt kwarg (that's what we can customize). This is used to set the LLMChain , which then goes to initialize the StuffDocumentsChain.

We can test the setup with a simple query to the vectorstore (see below for example vectorstore data) - you can see how the output is determined completely by the custom prompt:

chain(&quot;What color is mentioned in the document about cats?&quot;)[&#39;answer&#39;]
#&#39;\n\nDolphin leaps in sea\nGraceful and playful in blue\nJoyful in the waves&#39;

And memory is working correctly:

chain.memory
#ConversationBufferMemory(chat_memory=ChatMessageHistory(messages=[HumanMessage(content=&#39;What color is mentioned in the document about cats?&#39;, additional_kwargs={}), AIMessage(content=&#39;\n\nDolphin leaps in sea\nGraceful and playful in blue\nJoyful in the waves&#39;, additional_kwargs={})]), output_key=None, input_key=None, return_messages=True, human_prefix=&#39;Human&#39;, ai_prefix=&#39;AI&#39;, memory_key=&#39;chat_history&#39;)

Example vectorstore dataset with ephemeral ChromaDB instance:

from langchain.vectorstores import Chroma
from langchain.document_loaders import DataFrameLoader
from langchain.embeddings.openai import OpenAIEmbeddings

data = {
    &#39;index&#39;: [&#39;001&#39;, &#39;002&#39;, &#39;003&#39;], 
    &#39;text&#39;: [
        &#39;title: cat friend\ni like cats and the color blue.&#39;, 
        &#39;title: dog friend\ni like dogs and the smell of rain.&#39;, 
        &#39;title: bird friend\ni like birds and the feel of sunshine.&#39;
    ]
}

df = pd.DataFrame(data)
loader = DataFrameLoader(df, page_content_column=&quot;text&quot;)
docs = loader.load()

embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)

答案3

得分: 4

这是函数的一部分,代码不进行翻译。以下是该部分的翻译:

# 这是函数

def qasystem(query):

    loader = TextLoader("details.txt")
    documents = loader.load()
    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
    documents = text_splitter.split_documents(documents)

    vectordb = Chroma.from_documents(
        documents,
        embedding=OpenAIEmbeddings(),
        persist_directory='./data'
    )
    vectordb.persist()

    _template = """给定以下对话和后续问题,请重新表述后续问题,使其成为一个独立的问题,而不改变给定问题的内容。

    对话历史:
    {chat_history}
    后续输入:{question}
    独立问题:"""
    condense_question_prompt_template = PromptTemplate.from_template(_template)

    prompt_template = """您是一个提供有用信息的问答系统,确保您不回答与以下上下文无关的任何内容。您始终提供在给定上下文中可用的有用信息和细节。使用以下上下文片段来回答最后的问题。
    如果您不知道答案,只需说您不知道,不要试图编造答案。

    {context}

    问题:{question}
    有用的回答:"""
  
    qa_prompt = PromptTemplate(
        template=prompt_template, input_variables=["context", "question"]
    )

    memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
    llm = ChatOpenAI(temperature=0.1)
    question_generator = LLMChain(llm=llm, prompt=condense_question_prompt_template, memory=memory)
    doc_chain = load_qa_chain(llm, chain_type="stuff", prompt=qa_prompt)
    qa_chain = ConversationalRetrievalChain(
        retriever=vectordb.as_retriever(search_kwargs={'k': 6}),
        question_generator=question_generator,
        combine_docs_chain=doc_chain,
        memory=memory,

    )

    chat_history = []
    while True:
        result = qa_chain({'question': question, 'chat_history': chat_history})

        response = result['answer']
        chat_history.append((question, response))
        return result['answer']

希望这对你有所帮助。如果有任何其他疑问,请随时提出。

英文:

#This is the function

 def qasystem(query):

loader = TextLoader(&quot;details.txt&quot;)
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
documents = text_splitter.split_documents(documents)

vectordb = Chroma.from_documents(
    documents,
    embedding=OpenAIEmbeddings(),
    persist_directory=&#39;./data&#39;
)
vectordb.persist()

_template = &quot;&quot;&quot;Given the following conversation and a follow up question, rephrase the follow up question to be a 
standalone question without changing the content in given question.

Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:&quot;&quot;&quot;
condense_question_prompt_template = PromptTemplate.from_template(_template)

prompt_template = &quot;&quot;&quot;You are helpful information giving QA System and make sure you don&#39;t answer anything 
not related to following context. You are always provide useful information &amp; details available in the given context. Use the following pieces of context to answer the question at the end. 
If you don&#39;t know the answer, just say that you don&#39;t know, don&#39;t try to make up an answer. 

{context}

Question: {question}
Helpful Answer:&quot;&quot;&quot;

qa_prompt = PromptTemplate(
    template=prompt_template, input_variables=[&quot;context&quot;, &quot;question&quot;]
)

memory = ConversationBufferMemory(memory_key=&quot;chat_history&quot;, return_messages=True)
llm = ChatOpenAI(temperature=0.1)
question_generator = LLMChain(llm=llm, prompt=condense_question_prompt_template, memory=memory)
doc_chain = load_qa_chain(llm, chain_type=&quot;stuff&quot;, prompt=qa_prompt)
qa_chain = ConversationalRetrievalChain(
    retriever=vectordb.as_retriever(search_kwargs={&#39;k&#39;: 6}),
    question_generator=question_generator,
    combine_docs_chain=doc_chain,
    memory=memory,

)

chat_history = []
while True:
    result = qa_chain({&#39;question&#39;: question, &#39;chat_history&#39;: chat_history})

    response = result[&#39;answer&#39;]
    chat_history.append((question, response))
    return result[&#39;answer&#39;

答案4

得分: 0

使用ConversationBufferMemory时,我正在使用一个非常简单的测试来确认我的聊天机器人是否正常工作,即询问聊天机器人“我刚才问了什么问题”。

但我似乎总是得到相同的错误回答:

抱歉,但我无法访问您最初的问题,因为我是一个AI语言模型,没有跟踪先前交互的能力。您能再次重复您的问题吗?

尽管如此,似乎记忆在某种程度上是有效的,例如,如果我问非上下文相关的问题,它似乎能够正确回答。

是否有其他人遇到过这种异常情况?

我不确定是否需要改进提示,或者为什么会出现这种异常情况。

英文:

When using the ConversationBufferMemory I am using a very simple test to confirm whether memory is working on my chatbot, which is asking the chatbot "What was the first question I asked".

I always seems to get the same incorrect answer:

I'm sorry, but I don't have access to your initial question as I am an AI language model and I don't have the capability to track previous interactions. Could you please repeat your question?

Apart from this though it does seem that memory is working to an extent, for example if I ask non-contextual questions, it does seem to be able to answer correctly.

Has anyone else encountered this anomaly.

I'm not sure if I need to work on the prompting or why else this anomaly is arising.

huangapple
  • 本文由 发表于 2023年5月13日 10:43:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76240871.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定