基于langchain的多轮对话RAG

目录

概述

整体架构

代码实现

输出展示

参考


概述

        相比于单轮对话,多轮对话要考虑历史对话记录,大模型需要根据对话上下文去回答用户的问题。在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%。从这个评分来看,大部分观众对这部电影持正面评价。

然而,电影的评价存在一定的主观性,不同观众可能会有不同的看法。一些影评人对电影的剧情、角色发展和整体质量提出了批评,认为它在某些方面表现平庸或降智。例如,有影评人提到电影中“爱让我接受最真实的你”,这可能意味着电影在情感表达和角色塑造上得到了认可,但也有人批评电影的剧情走向和角色处理。

总的来说,《白蛇:浮生》在动画和爱情片类别中获得了相对较高的评价,但是否“好看”还需根据个人的观影喜好和期待来判断。建议在观看前,可以参考更多的影评和评分,以及查看预告片,以更好地了解电影的内容和风格,从而决定是否符合自己的观影需求。

参考

Conversational RAG | 🦜️🔗 LangChain

Qdrant | 🦜️🔗 LangChain

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值