- 作者:老汪软件技巧
- 发表时间:2024-08-24 07:03
- 浏览量:
在使用 LangChain 库的过程中,特别是处理对话式问答任务时,正确格式化对话历史记录(chat_history)是确保 ConversationalRetrievalChain 工作正常的关键。然而,这一过程可能会遇到一些坑,特别是在理解和遵循库的预期格式时。本文将分享我在这一过程中踩过的坑,并解释如何根据源码解决这些问题。
背景介绍
ConversationalRetrievalChain 是 LangChain 提供的用于对话式问答的强大工具。它结合了对话的上下文来检索信息并生成答案。在使用过程中,我遇到了关于 chat_history 格式的问题,导致链条无法正常工作。
踩过的坑1. 误解 chat_history 的格式要求
最初参考LangChain的官方文档的两个例子conversation_retrieval_chain
from langchain.chains import ConversationalRetrievalChain
from langchain_core.prompts import ChatPromptTemplate
condense_question_template = """
Given the following conversation and a follow up question, rephrase the follow up question to be a standalone question.
Chat History:
{chat_history}
Follow Up Input: {question}
Standalone question:"""
condense_question_prompt = ChatPromptTemplate.from_template(condense_question_template)
qa_template = """
You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer
the question. If you don't know the answer, say that you
don't know. Use three sentences maximum and keep the
answer concise.
Chat History:
{chat_history}
Other context:
{context}
Question: {question}
"""
qa_prompt = ChatPromptTemplate.from_template(qa_template)
convo_qa_chain = ConversationalRetrievalChain.from_llm(
llm,
vectorstore.as_retriever(),
condense_question_prompt=condense_question_prompt,
combine_docs_chain_kwargs={
"prompt": qa_prompt,
},
)
convo_qa_chain(
{
"question": "What are autonomous agents?",
"chat_history": "",
}
)
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
condense_question_system_template = (
"Given a chat history and the latest user question "
"which might reference context in the chat history, "
"formulate a standalone question which can be understood "
"without the chat history. Do NOT answer the question, "
"just reformulate it if needed and otherwise return it as is."
)
condense_question_prompt = ChatPromptTemplate.from_messages(
[
("system", condense_question_system_template),
("placeholder", "{chat_history}"),
("human", "{input}"),
]
)
history_aware_retriever = create_history_aware_retriever(
llm, vectorstore.as_retriever(), condense_question_prompt
)
system_prompt = (
"You are an assistant for question-answering tasks. "
"Use the following pieces of retrieved context to answer "
"the question. If you don't know the answer, say that you "
"don't know. Use three sentences maximum and keep the "
"answer concise."
"\n\n"
"{context}"
)
qa_prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
("placeholder", "{chat_history}"),
("human", "{input}"),
]
)
qa_chain = create_stuff_documents_chain(llm, qa_prompt)
convo_qa_chain = create_retrieval_chain(history_aware_retriever, qa_chain)
convo_qa_chain.invoke(
{
"input": "What are autonomous agents?",
"chat_history": [],
}
)
我将 chat_history 设置为一个简单的字符串或列表格式,例如:
chat_history = """
User: What are autonomous agents?
Assistant: Autonomous agents are systems that can perform tasks without human intervention by making decisions based on data and pre-defined rules or models.
User: How are they used in artificial intelligence?
Assistant: In AI, autonomous agents can be used for tasks such as robotics, simulation, and automated decision-making systems, where they learn and adapt to their environment.
"""
chat_history = [
{"role": "user", "content": "What are autonomous agents?"},
{"role": "assistant", "content": "Autonomous agents are systems..."}
]
这样做导致了一个 ValueError 错误:
ValueError: Unsupported chat history format: <class 'str'>. Full chat history: ...
2. 没有使用 .invoke() 方法
另一个问题是,我直接调用 ConversationalRetrievalChain 对象,而不是使用 .invoke() 方法。这样做在更新的 LangChain 版本中也是不可行的,导致进一步的错误提示。
问题解决过程1. 理解 chat_history 的正确格式
在查看 LangChain 的源码时,我发现 ConversationalRetrievalChain 的 _get_chat_history 方法中有一个格式化对话历史记录的函数。这个函数明确说明了 chat_history 的两种支持格式:
源码中 _get_chat_history 方法片段如下:
def _get_chat_history(chat_history: List[CHAT_TURN_TYPE]) -> str:
buffer = ""
for dialogue_turn in chat_history:
if isinstance(dialogue_turn, BaseMessage):
# 处理 BaseMessage 对象
elif isinstance(dialogue_turn, tuple):
# 处理 tuple 对象
else:
raise ValueError(
f"Unsupported chat history format: {type(dialogue_turn)}."
f" Full chat history: {chat_history} "
)
return buffer
由此可见,chat_history 必须是 BaseMessage 或 tuple 类型,否则将引发 ValueError 错误。
2. 调整 chat_history 格式
为了使 chat_history 符合 LangChain 的要求,我选择使用 tuple 格式:
chat_history = [
("What are autonomous agents?", "Autonomous agents are systems..."),
("How are they used in artificial intelligence?", "In AI, autonomous agents...")
]
或者,如果要使用 BaseMessage 对象:
from langchain.schema import BaseMessage
chat_history = [
BaseMessage(content="What are autonomous agents?", type="human"),
BaseMessage(content="Autonomous agents are systems...", type="assistant"),
BaseMessage(content="How are they used in artificial intelligence?", type="human"),
BaseMessage(content="In AI, autonomous agents...", type="assistant")
]
3. 使用 .invoke() 方法调用链
根据新版本 LangChain 的方法调用标准,应使用 .invoke() 方法来调用链条,而不是直接调用链对象。这可以避免与兼容性相关的错误。
response = convo_qa_chain.invoke(
{
"question": "Can you give examples of autonomous agents in real life?",
"chat_history": chat_history, # 使用正确的 `chat_history` 格式
}
)
总结
在使用 LangChain 的 ConversationalRetrievalChain 进行对话式问答时,正确格式化 chat_history 是关键。通过源码,我了解到 chat_history 必须是 BaseMessage 或 tuple 类型。同时,使用 .invoke() 方法调用链条,以确保代码的兼容性和功能正常。
希望这篇文章能够帮助你在使用 LangChain 时更好地理解如何处理对话历史记录的格式问题,以及如何根据源码调整代码避免错误!