iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
生成式 AI

T 大使 AI 之旅系列 第 21

【Day 21】數據檢索到動態記憶 - Memory 在 RAG 的幫助下延展 AI 的智慧

  • 分享至 

  • xImage
  •  

前情提要

上一篇文章實作了 Memory 的對話功能,有將對話紀錄存在變數中,也有存在 Upstash-Redis 的雲端資料庫。那今天第 21 天,來統整一下之前用過的技術,包含 API 呼叫 LLM 和 Embeddings Model、Qdrant 向量資料庫、Upstash-Redis 儲存對話紀錄、RAG、LCEL,將以上的技術皆納入今天的實作當中,當作是這 21 天的小成果!
https://ithelp.ithome.com.tw/upload/images/20240825/20168336WcYuktOqa9.png

實戰🔥

# 匯入套件
from langchain_ffm import ChatFormosaFoundationModel, FFMEmbedding
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_qdrant import QdrantVectorStore
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder, HumanMessagePromptTemplate, SystemMessagePromptTemplate, PromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_community.chat_message_histories import UpstashRedisChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from qdrant_client import QdrantClient
from qdrant_client.http import models
from qdrant_client.http.models import Distance
from operator import itemgetter
from dotenv import load_dotenv
load_dotenv()
import os

# 選擇模型和連線向量資料庫
llm = ChatFormosaFoundationModel(model="ffm-llama3-70b-chat", temperature=0.01)
embeddings = FFMEmbedding(model='ffm-embedding')
client = QdrantClient(url="http://localhost:6333")
collections_name = "ithome-RAG-With-Memory"

# 若 collection 存在則刪除 (現在沒有recreate函數)
if client.collection_exists(collection_name=collections_name):
	client.delete_collection(collection_name=collections_name)
else:
	pass

# 建立一個新的 collections
client.create_collection(
	collection_name=collections_name,
		vectors_config=
			models.VectorParams(
				size=1536,
				distance=Distance.COSINE,
			),
)

# langchain 連線 Qdrant 已存在的 collections
qdrant_vector_store = QdrantVectorStore.from_existing_collection(
	url="http://localhost:6333",
	collection_name=collections_name,
	embedding=embeddings,
)

# 切割 pdf 內容的函數
def split_pdf(pdf_path, pdf_chunk_size, pdf_chunk_overlap):
	pdf_loader = PyPDFLoader(pdf_path)
	pdf_docs = pdf_loader.load()
	pdf_text_splitter = RecursiveCharacterTextSplitter(chunk_size=pdf_chunk_size, chunk_overlap=pdf_chunk_overlap)
	pdf_split = pdf_text_splitter.split_documents(pdf_docs)
	return pdf_split

# 取得切割結果並匯入資料庫
pdf_data = split_pdf("https://web.metro.taipei/QRCode/Regulations%20for%20Use%20of%20the%20Taipei%20Metro%20System-Chinese.pdf", pdf_chunk_size=500, pdf_chunk_overlap=100)
qdrant_vector_store.add_documents(documents=pdf_data)

# 指定 retriever
retriever = qdrant_vector_store.as_retriever()

# RAG 的 prompt
qa_template = """你是一位回答問題的助手,使用以下檢索到的內容來回答問題。如果你不知道答案,就說你不知道。最多使用三個句子並保持答案簡潔。
{context}
"""

prompt = ChatPromptTemplate.from_messages(
	[
		SystemMessagePromptTemplate.from_template(qa_template),
		MessagesPlaceholder(variable_name="history"),
		HumanMessagePromptTemplate.from_template("{question}")
	]
)

# 將所有東西都 Chain 起來
chain = (
	RunnablePassthrough.assign(context = itemgetter("question") | retriever)
	| prompt
	| llm
)

# 聊天會話管理功能,可以管理多個對話
def get_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_session_history,
	input_messages_key="question",
	history_messages_key="history",
)

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

# 開始與 AI 對話
if __name__ == "__main__":
	session_id = "MRT"
	
	while True:
		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/20240825/20168336LeGz8eO67P.png
程式碼結果探討 🧐:

  • 程式碼中用到的都是前幾天實作有出現的東西,包括 Upstash-Redis、Qdrant 這些被 LangChain 整合進來的第三分工具,非常有效的幫助這個實作的完成。
  • 程式碼其中有比較大變動大地方是在 Chain 的那邊,原本 RAG 是 RunnableParallel({"context": retriever, "question": RunnablePassthrough()}),但因為 Memory 的關係,invoke 傳入的參數就不只一個,所以沒辦法使用這個方式。RunnablePassthrough.assign(context = itemgetter("question") | retriever) 這個方式就是從傳入的參數中取得 question 的部分,再傳遞給 Retriever 去檢索 question 對應的資料。
  • 從結果可以看到結合 Memory + RAG + LLM,已經算是一個功能很全面的聊天機器人了,如果有特定需求的話,給足 AI 特定的知識,像是網頁或者檔案等等,AI 可以最大程度的回答使用者的問題。

附上資料庫圖片

https://ithelp.ithome.com.tw/upload/images/20240825/20168336RSuB4u32Z8.png
https://ithelp.ithome.com.tw/upload/images/20240825/20168336XyReIhLbqX.png

結論

今天的實作成果結合了之前的生成式 AI 的技術和工具,我自己是覺得這些工具都是生成式 AI 必備的技術,尤其是 LangChain 框架,但 LangChain 缺點也很多,是讓人又愛又恨,以下我統整幾個我自己覺得的 LangChain 優缺點:

優點:

  1. 整合許多第三方工具,讓我們在開發上可以有很多選擇來作為我們的輔助。除了我實作中有用到的之外,我自己後續很想學的有 MongoDB、neo4j 等等。
  2. LCEL 架構是只需要明白流程的邏輯,就可以用最簡單的方式完成 Chains,支援平信運算和同時多個操作和查詢。
  3. Retriever 可以很快速簡單的檢索資料庫的內容,我自己是覺得 LangChain 的 Retriever 很好用也很直覺。(隔壁棚的 LlamaIndex 聽說也很強?但我沒用過所以還是先站在 LangChain 這邊)
  4. Agents 是讓 Chain 根據我們輸入的內容來決定要使用什麼工具,可以更靈活地控制和管理應用程序中的不同部分和功能。這個是我覺得比較進階的部分,對我自己來說也比較難,後續幾天會來實作。
  5. LangChain 家族的其他工具,像是 LangServe、LangGraph、LangSmith 這些都是開發的,但目前因為工作上沒有使用這些東西的需求,加上時間緊湊,可能沒辦法研究這些。會將這個技術納入未來學習目標!
  6. 進階的 RAG 工具,LangChain 有很多幫助提升 Retrieval 準確度的工具,像是 MultiQuery、# ContextualCompression 等等的,後續幾天也會來實作。

缺點:

  1. 亂七八糟的套件庫,不知道大家有沒有這種感覺,匯入的時候常常都要想一下這個套件是放在哪個套件下,譬如說你知道 RunnableWithMessageHistorylangchain_core 底下,但是後面的 runnable 和 history 真的不是這麼好記,每次使用的時候都要先去查一下。
  2. 學習的機會成本,我覺得學習 LangChain 對新手很不友善,他整個大框架真的需要一段時間來學習,當然一定也有大神覺得很簡單,但對於我來說在初期是很吃力,最近才好不容易跟 LangChain 熟一點。
  3. 最後一個是我覺得最討厭的部分,那就是版本間的差異過於劇烈。因為 Langchain 還在相關早期開發的階段,很多方面的成熟度都很不足,也導致版本和版本之間的差異過大。常常會發生一更新版本,原本的程式碼就無法運作,或者這些函數在某些版本後就被 LangChain Deprecated。

題外話🤣

來推薦一個我很喜歡也是我希望成為像他一樣厲害的一位全端工程師,甚至還會寫遊戲程式,真的是好好膜拜。他就是 陳南宗 ,連結是他的 FB 粉專,這是他的自我介紹,我只能說除了佩服還是佩服,我時不時就會看看他有沒有分享一些 AI 相關的新技術或者工具,分享給大家~

下一篇文章:什麼是模型微調,能吃嗎?


上一篇
【Day 20】讓 AI 記得你 - Memory 對話功能
下一篇
【Day 22】什麼是模型微調,能吃嗎?
系列文
T 大使 AI 之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言