iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
生成式 AI

T 大使 AI 之旅系列 第 20

【Day 20】讓 AI 記得你 - Memory 對話功能

  • 分享至 

  • xImage
  •  

前情提要

上一篇文章實作了 RAG 的實作,我們從資料庫檢索提示的資料給 AI,讓 AI 回答正確的內容。那接下來要再繼續看一個技術,就是 Memory 功能啦!
https://ithelp.ithome.com.tw/upload/images/20240824/20168336rghmpbJGC0.png

Memory 對話功能

這個技術很好理解是做什麼的,那就是實現在聊天的感覺。那為什麼會有這個功能的出現呢,因為每次使用 API 呼叫 LLM 都互不相關,每次呼叫都是新的一個對話,所以有了這個功能的誕生。

實戰🔥

Memory 入門

# 匯入套件
from langchain_ffm import ChatFormosaFoundationModel
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

# 建立模型和聊天提示模板
llm = ChatFormosaFoundationModel()
prompt = ChatPromptTemplate.from_messages(
	[
		SystemMessage(content="你是一位樂於助人的 AI 助手"),
		MessagesPlaceholder(variable_name="chat_history"),
		HumanMessagePromptTemplate.from_template("{input}")
	]
)

# Chain 起來
chain = prompt | llm

# 定義聊天函數
def chat_with_ffm(chain, user_input, chat_history):
	response = chain.invoke({
		"input": user_input,
		"chat_history": chat_history
	})
	return response.content

# 開始與 AI 對話
if __name__ == "__main__":

	chat_history = []

	while True:
		user_input = input("You: ")
		if user_input.lower() == "exit":
			break
		response = chat_with_ffm(chain, user_input, chat_history)
		chat_history.append(HumanMessage(content=user_input))
		chat_history.append(AIMessage(content=response))
		print(f"Assiatant: {response}")

https://ithelp.ithome.com.tw/upload/images/20240824/201683361NfG9or8gY.png
程式碼結果探討 🧐:

  • 其實大部分與呼叫 API 一樣,只是加上了對話的功能。
  • MessagesPlaceholder(variable_name="chat_history") 這個函數是用於在對話過程中插入消息歷史記錄。它允許模型在生成新的回應時,參考先前的對話內容,從而使回應更加符合上下文。variable_name="chat_history" 表示你在執行 chain.invoke() 時,需要提供一個名為 chat_history 的變數,其內容將會被插入到這個佔位符的位置。
  • 最後將 invoke 的部分封裝成一個函數,在主程式的地方被呼叫,並且使用 while 迴圈,就可以實作一個簡單的對話功能。

LCEL 架構 - RunnableWithMessageHistory

# 1.匯入套件
from langchain_ffm import ChatFormosaFoundationModel
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder
from langchain_core.messages import SystemMessage
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# 2.選擇模型和聊天提示模板
llm = ChatFormosaFoundationModel()
prompt = ChatPromptTemplate.from_messages(
	[
		SystemMessage(content="你是一位樂於助人的 AI 助手"),
		MessagesPlaceholder(variable_name="history"),
		HumanMessagePromptTemplate.from_template("{input}")
	]
)
chain = prompt | llm

# 3.聊天會話管理功能,可以管理多個對話
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
	if session_id not in store:
		store[session_id] = ChatMessageHistory()
	return store[session_id]

# 4.將聊天歷史與 LLM 串在一起
with_message_history = RunnableWithMessageHistory(
	chain,
	get_session_history,
	input_messages_key="input",
	history_messages_key="history",
)

# 5.定義聊天函數
def chat_with_ffm(user_input, session_id):
	response = with_message_history.invoke(
		{"input": user_input},
		config={"configurable": {"session_id": session_id}},
	)
	return response.content

# 6.開始與 AI 對話
if __name__ == "__main__":
	while True:
		session_id = "Sean"
		user_input = input("You: ")
		if user_input.lower() == "exit":
			break
		elif user_input.lower() == "clear":
			session_id = input("Enter a new session ID: ")
			user_input = input("You: ")
		response = chat_with_ffm(user_input, session_id)
		print(f"Assiatant: {response}")

https://ithelp.ithome.com.tw/upload/images/20240824/20168336MlzNVnnHn6.png
程式碼結果探討 🧐 (與註解編號一致):

  1. 大部分都跟之前的部分一樣,新增的套件是 BaseChatMessageHistory 作為聊天歷史的基類,類似之前自定義 LLM 的 BaseLanguageModelRunnableWithMessageHistory 是 LCEL 架構中包裝一個 LCEL 的 Chain,能夠管理消息歷史。ChatMessageHistory 是管理聊天歷史記錄。
  2. 這部分就跟第一部分的一模一樣。
  3. store = {} 是根據提供的 session_id 獲取或創建相應的聊天歷史記錄。這個函數確保了每個 id 有獨立的聊天歷史,並且這些歷史記錄可以被持續儲存和檢索。
  4. RunnableWithMessageHistoryget_session_history 與 Runnable 元素的 prompt 和 llm 串接起來,並設定 input 和 history 兩個變數分別代表 使用者輸入內容對話歷史紀錄 的傳遞。
  5. 這邊透過 session_id 來查詢對應的對話紀錄,以上下文的方式提供給 AI,使其可以生成相關的回應。
  6. 設定聊天的對話邏輯,預設一個 session_id,並且透過特定指令來另起一個 session_id,因此開啟一個新的對話。
  7. 結果可以看到 AI 有掌握到先前的對話紀錄,還可以透過我的提醒從中修正。另外切換一個 session_id 來開啟一個新的對話可以看到兩組對話紀錄是互不相關的!

Upstash-Redis

前面的兩個實作都是存在我們所設定的環境變數中,那如果想要將對話紀錄永久保存的話,我一開始的作法是使用 MySQL,但是要自己寫很多連線和新增查詢的函數。其實 LangChain 有整合很多資料庫可以使用,而且存入或取出資料都很方便,那我這邊選擇 Upstash-Redis,他每天有 10k 的免費 tokens,我自己是覺得如果有儲存對話紀錄的需求的話很值得使用。他們家最近也出了向量資料庫的服務,有機會再來玩看看,但目前是 Qdrant 死忠用戶😆
-> 點我看 Upstash-Redis

建立 Database

註冊並登入之後就可以來的 Upstash-Redis 的首頁,非常簡約風格~點選 Create database 即可進入設定資料庫頁面。
https://ithelp.ithome.com.tw/upload/images/20240825/20168336iGwY4lJuhL.png

設定 Database 與選擇方案

這個其實就是 Upstash-Redis 他們家的雲端服務,需要選一個區域和設定名字,區域我自己試的時候是沒有什麼區域是有限制的,不像 Azure 不同區域有一堆服務限制。那麼在方案的部分當然是選擇免費版囉~~
https://ithelp.ithome.com.tw/upload/images/20240825/20168336ROzAKOguxt.jpg

REST API

建立資料庫完成之後,要來取得連線資料庫的參數,有 URL 和 TOKEN。所以進到資料庫裡面之後,往下滾找到 REST API 的地方,先將這兩個需要的參數儲存起來,後續要在程式碼中調用。
https://ithelp.ithome.com.tw/upload/images/20240825/201683360IBVpGVlsi.png

實戰🔥

開啟與儲存對話紀錄

# 匯入套件
from langchain_ffm import ChatFormosaFoundationModel
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder
from langchain_core.messages import SystemMessage
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories.upstash_redis import UpstashRedisChatMessageHistory
from dotenv import load_dotenv
load_dotenv()
import os

# 選擇模型和聊天提示模板
llm = ChatFormosaFoundationModel()
prompt = ChatPromptTemplate.from_messages(
	[
		SystemMessage(content="你是一位樂於助人的 AI 助手"),
		MessagesPlaceholder(variable_name="history"),
		HumanMessagePromptTemplate.from_template("{input}")
	]
)
chain = prompt | llm

# 從 Upstash-Redis 資料庫中取得和儲存對應 session_id 的對話紀錄
def get_upstash_session_history(session_id: str) -> BaseChatMessageHistory:
	return UpstashRedisChatMessageHistory(
		url=os.environ['UPSTASH_REDIS_REST_URL'],
		token=os.environ['UPSTASH_REDIS_REST_TOKEN'],
		session_id=session_id
)

# 將聊天歷史與 LLM 串在一起
with_message_history = RunnableWithMessageHistory(
	chain,
	get_upstash_session_history,
	input_messages_key="input",
	history_messages_key="history",
)

# 定義聊天函數
def chat_with_ffm(user_input, session_id):
	response = with_message_history.invoke(
		{"input": user_input},
		config={"configurable": {"session_id": session_id}},
	)
	return response.content

# 開始與 AI 對話
if __name__ == "__main__":
	while True:
		session_id = input("Enter a new session ID: ")
		user_input = input("You: ")
		
		if user_input.lower() == "exit":
			break

		elif user_input.lower() == "clear":
			session_id = input("Enter a new session ID: ")
			user_input = input("You: ")
		
		response = chat_with_ffm(user_input, session_id)
		print(f"Assiatant: {response}\n")

https://ithelp.ithome.com.tw/upload/images/20240825/20168336hzqyoP9VFj.png
https://ithelp.ithome.com.tw/upload/images/20240825/20168336Qp8BuNmJqq.png
程式碼結果探討 🧐:

  • 可以看到 AI 不僅有針對對話紀錄來回答問題,也順利存進 Upstash-Redis,也有分不同的 Session。
  • 程式碼的部分其實變動的不多,只是將原本的 ChatMessageHistory() 的改成 Upstash-Redis,他們都同樣是 ChatMessageHistory 物件。

查看對話紀錄

from langchain_community.chat_message_histories import UpstashRedisChatMessageHistory
from dotenv import load_dotenv
load_dotenv()
import pprint
import os

URL = os.environ["UPSTASH_REDIS_REST_URL"]
TOKEN = os.environ["UPSTASH_REDIS_REST_TOKEN"]

history = UpstashRedisChatMessageHistory(
	url=URL, token=TOKEN, session_id="Sean"
)

pprint.pprint(history.messages)

https://ithelp.ithome.com.tw/upload/images/20240825/20168336hp6vxawNVA.png
程式碼結果探討 🧐:

  • 只有使用函數 messages 即可查詢到對應 session 下的所以對話紀錄。

補充

如果希望對話紀錄存在超過一定時間後自己刪除的話,可以設定 ttl 參數。ttl 是以秒為單位,我這邊設為 10,代表 10 秒後這筆對話會從資料庫自行刪除。

UpstashRedisChatMessageHistory(
	url=URL, token=TOKEN, ttl=10, session_id="test"
)

結論

今天使用一個簡單的 list 來儲存對話紀錄,也有使用 LCEL 內建的函數搭配 dict 來儲存對話紀錄,確實使用 LCEL 還多了可以管理不同對話紀錄的功能,但除了這部分實現的內容其實是一樣的。最後實作了一個將對話紀錄存放在雲端,透過 Upstash-Redis 結合 LangChain 用起來是很直覺很方便!

題外話🤣

今天喝酒有點宿醉哈哈哈~但還是打起精神來寫文章!

下一篇文章:數據檢索到動態記憶 - Memory 在 RAG 的幫助下延展 AI 的智慧


上一篇
【Day 19】RAG 狂想曲 - AI 不再迷路
下一篇
【Day 21】數據檢索到動態記憶 - Memory 在 RAG 的幫助下延展 AI 的智慧
系列文
T 大使 AI 之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言