【RAG 專案實戰 04】新增多輪對話能力

青松^_^發表於2024-11-23

【RAG 專案實戰 04】新增多輪對話能力


NLP Github 專案:

  • NLP 專案實踐fasterai/nlp-project-practice

    介紹:該倉庫圍繞著 NLP 任務模型的設計、訓練、最佳化、部署和應用,分享大模型演算法工程師的日常工作和實戰經驗

  • AI 藏經閣https://gitee.com/fasterai/ai-e-book

    介紹:該倉庫主要分享了數百本 AI 領域電子書

  • AI 演算法面經fasterai/nlp-interview-handbook#面經

    介紹:該倉庫一網打盡網際網路大廠NLP演算法面經,演算法求職必備神器

  • NLP 劍指Offerhttps://gitee.com/fasterai/nlp-interview-handbook

    介紹:該倉庫彙總了 NLP 演算法工程師高頻面題


[!NOTE] 新增多輪對話能力

  1. 儲存對話歷史
  2. 新增 session_id
  3. 提示模板中新增 chat_history
  4. RunnableWithMessageHistory 包裝 Chain 新增對話歷史能力
  5. 配置中使用 session_id 進行大模型互動,可以根據 session_id 區分不同使用者的對話歷史

一、新增多輪對話能力

01 儲存對話歷史

# 儲存對話歷史
store = {}


def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

02 新增 session_id

# 新增 session_id
cl.user_session.set('session_id', 'abc2')

03 在提示模板中新增 chat_history

# 新增 session_id
cl.user_session.set('session_id', 'abc2')

# 提示模板中新增 chat_history
prompt = ChatPromptTemplate.from_messages(
	[
		(
			"system",
			"You're a very knowledgeable historian who provides accurate and eloquent answers to historical questions.",
		),
		MessagesPlaceholder("chat_history"),
		("human", "{question}"),
	]
)

04 使用 RunnableWithMessageHistory 包裝 Chain

# 獲取當前的 session_id
session_id = cl.user_session.get("session_id")

# 用 RunnableWithMessageHistory 包裝 Chain 新增對話歷史能力
runnable_with_history = RunnableWithMessageHistory(
	runnable,
	get_session_history,
	input_messages_key="question",
	history_messages_key="chat_history",
)

05 使用 session_id 進行大模型互動

msg = cl.Message(content="")

# 配置中使用 session_id 進行大模型互動
async for chunk in runnable_with_history.astream(
		{"question": message.content},
		config=RunnableConfig(configurable={"session_id": session_id},
							  callbacks=[cl.LangchainCallbackHandler()])
):
	await msg.stream_token(chunk)

await msg.send()

Tips:用 RunnableWithMessageHistory 包裝 Chain 新增對話歷史能力,不能將其插入到 Chain 中

二、效果展示

多輪能力展示

🌈 這是本人最喜歡的一首詩,它陪我走過不少困難時光。以此與諸君共勉之!祝君前程似錦~

《小松》 -- 唐 杜荀鶴
自小刺頭深草裡,而今漸覺出蓬蒿。
時人不識凌雲木,直待凌雲始道高。

三、完整程式碼

# @Author:青松
# 公眾號:FasterAI
# 理想使命:讓每個人的AI學習之路走的更容易些,若我的經驗能為你前行的道路增添一絲輕鬆,我將倍感榮幸🌈🌈🌈 讓知識傳遞更容易,用知識讓生活更美好!
# Python, version 3.10.14
# Pytorch, version 2.3.0
# Chainlit, version 1.1.301

import chainlit as cl
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser
from langchain.schema.runnable import Runnable
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_community.chat_models import QianfanChatEndpoint
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.prompts import MessagesPlaceholder
from langchain_core.runnables import RunnableConfig
from langchain_core.runnables.history import RunnableWithMessageHistory


model = QianfanChatEndpoint(
    streaming=True,
    model="ERNIE-Speed-8K",
)

# 儲存對話歷史
store = {}


def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]


@cl.password_auth_callback
def auth_callback(username: str, password: str):
    """ 持久化客戶端聊天曆史程式碼,不需要請刪除 """
    if (username, password) == ("admin", "admin"):
        return cl.User(
            identifier="admin", metadata={"role": "admin", "provider": "credentials"}
        )
    else:
        return None


@cl.on_chat_start
async def on_chat_start():
    """ 監聽會話開始事件 """
    # todo: 新增 FasterAI 知識星球圖片以及 FastAI 知識庫地址
    image = cl.Image(url="https://qingsong-1257401904.cos.ap-nanjing.myqcloud.com/wecaht.png")

    # 傳送一個圖片
    await cl.Message(
        content="**青松** 邀你關注 **FasterAI**, 讓每個人的 AI 學習之路走的更容易些!開啟 AI 學習、面試快車道 **(^_^)** ",
        elements=[image],
    ).send()

    # 新增 session_id
    cl.user_session.set('session_id', 'abc2')

    # 提示模板中新增 chat_history
    prompt = ChatPromptTemplate.from_messages(
        [
            (
                "system",
                "你是一箇中國古詩詞專家,能準確的一字不差的背誦很多古詩詞,請用你最大的能力來回答使用者的問題。",
            ),
            MessagesPlaceholder("chat_history"),
            ("human", "{question}"),
        ]
    )
    runnable = prompt | model | StrOutputParser()
    cl.user_session.set("runnable", runnable)


@cl.on_message
async def on_message(message: cl.Message):
    """ 監聽使用者訊息事件 """

    runnable = cl.user_session.get("runnable")  # type: Runnable

    # 獲取當前的 session_id
    session_id = cl.user_session.get("session_id")

    # 用 RunnableWithMessageHistory 包裝 Chain 新增對話歷史能力
    runnable_with_history = RunnableWithMessageHistory(
        runnable,
        get_session_history,
        input_messages_key="question",
        history_messages_key="chat_history",
    )

    msg = cl.Message(content="")

    # 配置中使用 session_id 進行大模型互動
    async for chunk in runnable_with_history.astream(
            {"question": message.content},
            config=RunnableConfig(configurable={"session_id": session_id},
                                  callbacks=[cl.LangchainCallbackHandler()])
    ):
        await msg.stream_token(chunk)

    await msg.send()



【動手學 RAG】系列文章:

  • 【RAG 專案實戰 01】在 LangChain 中整合 Chainlit
  • 【RAG 專案實戰 02】Chainlit 持久化對話歷史
  • 【RAG 專案實戰 03】優雅的管理環境變數
  • 【RAG 專案實戰 04】新增多輪對話能力
  • 【RAG 專案實戰 05】重構:封裝程式碼
  • 【RAG 專案實戰 06】使用 LangChain 結合 Chainlit 實現文件問答
  • 【RAG 專案實戰 07】替換 ConversationalRetrievalChain(單輪問答)
  • 【RAG 專案實戰 08】為 RAG 新增歷史對話能力

本文由mdnice多平臺釋出

相關文章