目录
概述
相比于单轮对话,多轮对话要考虑历史对话记录,大模型需要根据对话上下文去回答用户的问题。在RAG的场景中,通常需要通过问题去召回和问题相关的知识,再将知识和问题交给大模型去润色回复用户。但是有没有想过,如果这个问题指代了历史消息的某个重要对象(通常是问题的核心)时,就不能召回有用的知识,大模型的回答效果不会太好。解决办法是,让大模型基于历史消息对用户最新的问题进行改写,补充指代对象,然后再去进行知识的召回,效果会大大改善。以下将以豆瓣电影《白蛇:浮生》作为大模型的知识补充来进行多轮对话。
整体架构
- history_aware_retriever将用户的当前输入和历史消息通过大模型来进行改写,从而去进行文档的召回
- question_answer_chain会基于召回的文档、历史消息、当前输入给到大模型进行润色输出
- embedding的部分采用BCE模型,embedding+ranking可以达到SOTA水平,本案例只有embedding,后续可以考虑将召回再过一层ranking
- 向量库采用的是qdrant,在一些数据集和指标上也可以达到SOTA水平,本地部署可以采用内存、持久化到磁盘和docker模式,氪金的话就可以上它的cloud
- 模型采用的是Qwen/Qwen2-7B-Instruct,采用vllm部署openai接口形式提供服务
代码实现
from typing import List
from langchain_openai.chat_models import ChatOpenAI
from langchain.chains.history_aware_retriever import create_history_aware_retriever
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance
from langchain_qdrant import QdrantVectorStore
from langchain.prompts.chat import ChatPromptTemplate
from langchain.prompts import MessagesPlaceholder
from langchain_core.documents import Document
from langchain_huggingface.embeddings import HuggingFaceEmbeddings
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.document_transformers import Html2TextTransformer
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains.retrieval import create_retrieval_chain
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables import RunnableWithMessageHistory
import os
import sys
sys.path.append(os.path.abspath(os.pardir))
from global_config import MODEL_CACHE_DIR
def get_chat_llm():
chat_model = ChatOpenAI(
model="Qwen2-7B-Instruct",
openai_api_key="empty",
openai_api_base="http://localhost:8000/v1",
max_tokens=2048,
temperature=0
)
return chat_model
def get_retriever(docs: List[Document]):
embeddings = HuggingFaceEmbeddings(model_name=os.path.join(MODEL_CACHE_DIR, "maidalun/bce-embedding-base_v1"))
client = QdrantClient(":memory:")
# if not exists then create
if not client.collection_exists("rag_collection"):
# create collection
client.create_collection(
"rag_collection",
vectors_config=VectorParams(
size=len(embeddings.embed_query("hello world")),
distance=Distance.COSINE
)
)
vector_store = QdrantVectorStore(
client=client, collection_name="rag_collection", embedding=embeddings
)
vector_store.add_documents(docs)
return vector_store.as_retriever(search_kwargs={"k": 5})
def get_contextualize_question_prompt():
"""
基于历史记录来改写用户问的问题
:return:
"""
system_prompt = """\
请根据聊天历史和最后用户的问题,改写用户最终提出的问题。
你只需要改写用户最终的问题,请不要回答问题
没有聊天历史则将用户问题直接返回,有聊天历史则进行改写
"""
contextualize_question_prompt = ChatPromptTemplate([
("system", system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}")
])
return contextualize_question_prompt
def get_answer_prompt():
system_prompt = """\
你是一个问答任务的助手,请依据以下检索出来的信息去回答问题:
{context}
"""
qa_prompt = ChatPromptTemplate([
("system", system_prompt),
MessagesPlaceholder("chat_history"),
("human", "{input}")
])
return qa_prompt
def get_session_history(session_id:str) -> ChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
if __name__ == '__main__':
llm = get_chat_llm()
# loader
loader = WebBaseLoader(
web_path=("https://movie.douban.com/subject/36463483/?from=showing",),
encoding="utf-8"
)
docs = loader.load()
html2test_transformer = Html2TextTransformer()
docs = html2test_transformer.transform_documents(docs)
# text split
text_spliter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100)
docs = text_spliter.split_documents(docs)
# retriever
retriever = get_retriever(docs)
# contextualize question
question_prompt = get_contextualize_question_prompt()
history_aware_retriever = create_history_aware_retriever(llm, retriever, question_prompt)
# qa chain
qa_prompt_template = get_answer_prompt()
qa_chain = create_stuff_documents_chain(llm, qa_prompt_template)
rag_chain = create_retrieval_chain(history_aware_retriever, qa_chain)
# with history
store = {}
conversational_rag_chain = RunnableWithMessageHistory(
rag_chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
output_messages_key="answer"
)
# 改写用户内容部分
contextualize_question_chain = RunnableWithMessageHistory(
question_prompt | llm,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history"
)
while(True):
inputs = input("请输入:")
if inputs == "end":
break
res = contextualize_question_chain.invoke({
"input": inputs
}, config={
"configurable": {"session_id": "test456"}
})
print("改写后内容:\n" + res.content)
res = conversational_rag_chain.invoke({
"input": inputs
}, config={
"configurable": {"session_id": "test123"}
})
print("回答:\n" + res["answer"])
输出展示
请输入:介绍一下《白蛇:浮生》这部电影
改写后内容:
请简要概述一下电影《白蛇:浮生》的内容
回答:
《白蛇:浮生》是一部中国大陆制作的动画电影,属于“白蛇”系列的最终章,由陈健喜和李佳锴执导,王微担任编剧。该电影的主演包括张喆、杨天翔、唐小喜、张赫、刘琮、郑小璞、马程、林强和李楠,以及李佳锴。《白蛇:浮生》的类型涵盖了喜剧、爱情、动画和奇幻,讲述的是在南宋临安,白蛇(小白)在五百年后终于找到了许仙(阿宣)的转世,两人在断桥相遇,开始了在人间的平凡生活。然而,杭州城中发生的一系列怪事,金山寺的法海前来除妖,意外揭开了小白和小青(小青)的蛇妖身份,许仙目睹了小白的巨蟒形态,引发了一场生死情劫。
电影的上映日期为2024年8月10日(中国大陆),片长为133分钟。在豆瓣上,该电影的评分是7.1分,基于68261人的评价,其中5星评价占比16.3%,4星评价占比36.0%,3星评价占比37.0%,2星评价占比8.9%,1星评价占比1.8%。该电影被认为在动画片中好于75%,在爱情片中好于84%。
《白蛇:浮生》的剧情简介提到了故事发生在南宋临安,小白与许仙在断桥相遇,开始了平凡的人间生活。然而,随着杭州城中发生的怪事,金山寺的法海前来除妖,意外揭露了小白和小青的蛇妖身份,引发了一场生死情劫。电影探讨了生死、爱情与命运的主题。
在豆瓣上,有700篇关于《白蛇:浮生》的影评,涵盖了从正面到负面的各种观点。影评人杉姐评价说:“我中国人的DNA,都被它唤醒了!”这表明电影在某些方面成功地触动了观众的情感。同时,也有影评人对电影的剧情、角色发展和整体质量提出了批评,认为它在某些方面表现平庸或降智。
《白蛇:浮生》的视频和图片部分包含了预告片和电影截图,供观众预览和欣赏。此外,还有其他与“白蛇”系列相关的电影推荐,如《聊斋:兰若寺》、《白蛇2:青蛇劫起》、《新神榜:杨戬》、《长安三万里》、《落凡尘》、《穿过月亮的旅行》、《八戒之天蓬下界》、《雄狮少年2》、《大雨》和《我想活出怎样的人生》等。
===================================================
请输入:这部电影是白蛇系列的第几部
改写后内容:
白蛇系列电影中,《白蛇:浮生》是哪一部作品
回答:
《白蛇:浮生》是“白蛇”系列的最终章,标志着该系列的完结。在时间顺序上,它应该被视为三部曲的“正传”,前有《白蛇:缘起》作为“前传”,后有《白蛇2:青蛇劫起》作为“后传”。因此,《白蛇:浮生》是“白蛇”系列的第三部电影。
====================================================
请输入:这个电影好看吗
改写后内容:
您觉得电影《白蛇:浮生》是否值得一看呢
回答:
《白蛇:浮生》在豆瓣上获得了7.1的评分,基于68261人的评价,这表明它在动画片中好于75%,在爱情片中好于84%。从这个评分来看,大部分观众对这部电影持正面评价。
然而,电影的评价存在一定的主观性,不同观众可能会有不同的看法。一些影评人对电影的剧情、角色发展和整体质量提出了批评,认为它在某些方面表现平庸或降智。例如,有影评人提到电影中“爱让我接受最真实的你”,这可能意味着电影在情感表达和角色塑造上得到了认可,但也有人批评电影的剧情走向和角色处理。
总的来说,《白蛇:浮生》在动画和爱情片类别中获得了相对较高的评价,但是否“好看”还需根据个人的观影喜好和期待来判断。建议在观看前,可以参考更多的影评和评分,以及查看预告片,以更好地了解电影的内容和风格,从而决定是否符合自己的观影需求。