iT邦幫忙

2024 iThome 鐵人賽

DAY 19
0
生成式 AI

2024 年用 LangGraph 從零開始實現 Agentic AI System系列 第 19

【Day 19】- LangGraph 的記憶機制:提升 AI 助理的上下文理解能力

  • 分享至 

  • xImage
  •  

摘要
這篇文章探討了 LangGraph 這個框架,它用於提升 AI 助理的記憶能力,並能有效管理對話歷史。文章首先說明了 AI 助理需要記憶功能的原因,以及記憶功能如何改善使用者體驗。接著文章深入解析 LangGraph 的核心機制,Checkpointer,它是一個狀態保存器,能捕捉並儲存圖形的完整狀態,就像 AI 的記憶儲存器。文章也介紹了三種不同的記憶類型:對話歷史、知識庫和語義緩存,並深入探討它們的技術實現方式。文章也說明了如何有效管理對話歷史,透過過濾消息和修剪消息技術,可以精確控制傳遞給 AI 模型的上下文信息,避免性能問題,並確保回應的相關性和準確性。最後,文章總結了使用 LangGraph 來提升 AI 助理記憶能力和對話管理能力的關鍵優點。

引言:記憶能力,為何重要且如何改變互動體驗

開始之前,我想先問看看,為什麼需要替 Agent 添加記憶能力?

想像一下,如果你每次跟朋友聊天,他都忘記你之前說過的話,那會是什麼感覺?超級尷尬吧!AI 助理也一樣,沒有記憶的話,每次對話都像是從頭開始。但是,具體來說,有記憶的 AI 助理能做什麼特別的事情嗎?舉個例子吧:假設你告訴 AI 助理你對花生過敏。有了記憶,下次你問「今天午餐吃什麼好?」它就會避免推薦花生相關的食物。

img

那麼,給 AI 加上記憶功能,最大的好處是什麼呢?
你有沒有試過要完成一個複雜的專案?像是籌備婚禮或是裝修房子?有記憶的 AI 助理可以幫你記住所有細節,跟進每個任務,甚至根據過去的經驗給你建議。它就像是你的超級助理,隨時隨地幫你分擔腦力,讓你不會漏掉任何重要的事情。這樣一來,你就可以把精力集中在真正重要的事情上,是不是很棒?

1. 對話中的隱藏智慧:解析生成式 AI 的三種記憶

你有沒有想過,如果 AI 助理真的能夠「記住」我們的對話,會是什麼樣子?讓我們一起來看看,在日常生活中,AI 是如何運用不同類型的「記憶」來幫助我們的。
想像一下這樣的場景:

小美:「嗨,我想計劃一趟歐洲旅行。」
AI:「你好小美!記得你上次提到想看北極光,要將北歐納入行程嗎?」
小美:「對,不過我也想去溫暖的地方。」
AI:「明白了。考慮到你的喜好和現在是夏天,建議你可以先去義大利,然後 10 月去挪威看北極光。義大利有豐富的藝術和美食,符合你的興趣。挪威的特羅姆瑟是觀賞北極光的好地方。根據你之前提到的預算,這趟旅行大約需要 3000-4000 歐元,不包括機票。需要我幫你規劃詳細行程嗎?」
小美:「聽起來不錯!謝謝你的建議。」

看完這個對話,你是否好奇 AI 是如何做到既了解小美的需求,又能提供專業建議的呢?讓我們來解析一下 AI 在這段對話中運用的三種不同「記憶」類型:

1.1 AI 的三種記憶類型

  1. 對話歷史(Conversation History):記錄 AI 系統與使用者之間的歷史對話內容,使 AI 能夠回顧先前的交互,保持對話的連貫性。

  2. 知識庫(Knowledge Base):儲存大量結構化的事實和資訊,提供 AI 回答問題和做出決策所需的基礎知識。

  3. 語義緩存(Semantic Memory Buffer):短期儲存並處理複雜概念的機制,使 AI 能夠在當前對話中進行多步驟推理和概念關聯。

現在,讓我們來解析一下 AI 在這段對話中如何運用這三種「記憶」類型:

  • 對話歷史(Conversation History):還記得 AI 如何提到小美之前想看北極光嗎?這就是對話歷史的應用。AI 能夠回顧之前的對話,確保建議的連貫性和個人化。
  • 知識庫(Knowledge Base):AI 是如何知道夏天去義大利,秋天去挪威看北極光比較好呢?這些資訊來自 AI 的知識庫,儲存了大量的旅遊相關資訊。
  • 語義緩存(Semantic Memory Buffer):你注意到 AI 是如何結合小美想看北極光,又想去溫暖地方的需求,同時考慮季節因素來制定建議的嗎?這種複雜的推理反映了語義緩存的作用。

1.2 AI 記憶類型的實現

對於那些對技術細節感興趣的讀者,以下是每種記憶類型的簡要技術說明:

  1. 對話歷史(Conversation History):
  • 實現方式:通常使用序列數據結構(如列表或隊列)儲存對話中的每條消息。
  • 儲存方案:可以使用 NoSQL 資料庫(如 MongoDB)來實現高效的讀寫操作。
  • 應用:在生成回應時,將歷史對話作為上下文輸入到語言模型中。
  1. 知識庫(Knowledge Base):
  • 實現方式:可以使用圖形資料庫(如 Neo4j)或關係型資料庫(如 PostgreSQL)。
  • 更新機制:結合爬蟲技術和人工審核,定期更新和驗證資訊。
  • 應用:使用向量資料庫(如 Pinecone)進行高效的相似度搜索,實現知識檢索。
  1. 語義緩存(Semantic Memory Buffer):
  • 實現方式:使用神經網路模型(如 Transformer)來編碼和處理語義資訊。
  • 工作原理:將對話內容轉換為向量表示,在向量空間中進行語義操作。
  • 應用:使用注意力機制(Attention Mechanism)來實現複雜的推理和關聯分析。

2. Checkpointer:LangGraph 狀態保存的核心機制

在 AI 應用開發過程中,跨多次互動共享上下文的能力至關重要。LangGraph 透過 Checkpointer 為所有 StateGraph 提供了這項關鍵功能。接下來我們將深入探討這個機制的運作原理。

Checkpointer 的本質

Checkpointer 本質上是一個狀態保存器。它的主要職責是在 StateGraph 執行過程中,於特定時間點捕捉並儲存圖的完整狀態。這個過程確保了系統狀態的完整保存。

實作方式

在 LangGraph 中實作 Checkpointer 相當直觀:

  1. 首先,開發者需要初始化一個 Checkpointer 實例。
  2. 接著,在編譯圖時將這個 Checkpointer 實例作為參數傳入。像這樣compile(checkpointer=custom_checkpointer)
my_checkpointer = CustomCheckpointer()
graph.compile(checkpointer=my_checkpointer)

Checkpointer 的設計具有高度的靈活性,支援多種後端儲存方案。這包括從簡單的記憶體存儲到更複雜的資料庫系統,如 SQLite。這種多樣性使開發者能夠根據應用的具體需求選擇最適合的儲存方式。

雖然 Checkpointer 支援多種後端儲存方案,包括記憶體存儲和 SQLite,但對於更大規模或需要更複雜查詢功能的應用,外部資料庫如 MongoDB 可能是更好的選擇。我們將在下一章節深入探討這個話題。

LangGraph 中的狀態保存過程

在 LangGraph 中,狀態保存是通過內建的持久層來實現的。這個機制允許在執行過程中的不同時間點儲存圖形的狀態。以下是狀態保存過程的關鍵要素:

每當 LangGraph 完成一次狀態更新,也就是圖中所有節點都處理完自己的工作並更新狀態後,檢查點儲存器就會立即執行,將最新的狀態保存下來。這種方式確保了在執行過程中的每個重要階段,都能保存圖形的完整狀態。

checkpointer 就像是使用者的歷史紀錄,會幫你存檔對話進度,下次聊天直接讀檔繼續,不用重頭來過喔。

3. 狀態與檢查點:LangGraph 中的關鍵概念及其差異

在 LangGraph 的架構中,「狀態」和「檢查點」是兩個核心但截然不同的概念。讓我們深入探討它們的特性及主要差異:

3.1 狀態 (State)

狀態代表應用程式在任一給定時刻的當前快照。它是一個動態數據結構,隨著圖形的執行持續更新。狀態通常具備以下特徵:

  • 動態性:狀態會隨每個節點的執行而變化,反映當前的應用程式狀態。
  • 共享資料結構:狀態為一共享資料結構,通常採用 Python 的 TypedDict 或 Pydantic BaseModel 表示。
  • 上下文維護:狀態協助維護應用程式的上下文,使每個節點能基於當前狀態進行操作。

3.2 檢查點 (Checkpointer)

檢查點則是一種持久化機制,用於保存和恢復圖形的狀態。檢查點的主要功能包含:

  • 持久化:檢查點允許將狀態保存至持久儲存中,如數據庫或文件系統,即使應用程式重啟,先前的狀態亦可被恢復。
  • 回溯與恢復:檢查點使用者能將圖形狀態回溯至先前的某個時間點,對錯誤恢復和版本控制極為有用。
  • 版本控制:檢查點系統支援版本控制,允許使用者探索不同的執行路徑,並保持對 AI 行為的完全掌控。

4. Checkpoint 方法選擇策略

實作 Checkpoint 機制時,我們有幾種不同的選擇,每種方法都有其特定的應用場景和優點。目前主要有以下幾種方式:

  1. MemorySaver:這是一種利用記憶體來儲存圖狀態的方法,使用字典結構來管理資料。特別適合需要快速存取,但不需要長期保存資料的情況。
  2. SqliteSaver:這個方法讓我們可以將狀態儲存到本機或記憶體中的 SQLite 資料庫。它提供了一個折衷方案,既有資料庫的結構化優勢,又保持了較為簡單的設定過程。
  3. 外部資料庫:若需要更強大的功能,我們可以選擇如 PostgreSQL、MongoDB 或 Redis 等外部資料庫系統。這些選項特別適合處理大規模資料和複雜查詢的需求。

那麼,我們要如何在實際應用中有效地管理聊天歷史紀錄呢?

在本章節中,我們會專注於使用記憶體保存方式來示範 Checkpointer 的應用,特別是在管理聊天歷史紀錄方面。此外,我們還會探討兩種 LangChain 機制,這些機制在處理大量聊天訊息時特別有效。透過這種方式,我們不僅能夠了解 Checkpointer 的基本操作,還能學習如何在面對資料量增加時維持系統的效能和穩定性。

5. 實踐:利用 MemorySaver 實現對話歷史管理

利用記憶體來儲存圖狀態的方法,使用字典結構來管理資料,這種儲存方式的訪問和檢索速度非常快,因為它不涉及磁碟 I/O 操作。

我們來編譯圖,並加入記憶功能:

from langgraph.checkpoint.memory import MemorySaver

workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("action", tool_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges(
    "agent",
    should_continue,
)
workflow.add_edge("action", "agent")

memory = MemorySaver()
app = workflow.compile(checkpointer=memory)

現在,我們來試試看這個有記憶功能的AI 代理:

img

看到了嗎?AI 記住了小明的名字!但如果我們用不同的 thread_id,AI 就會忘記之前的對話喔。

6. Thread_ID:LangGraph 記憶能力中多對話流的管理機制

在 LangGraph 系統中,thread_id 是一個與特定執行緒相關聯的唯一識別符。每個執行緒代表一個獨立的互動或對話流程,使系統能夠同時管理多個對話或處理程序。
thread_id 的定義與應用:

thread_id 是由檢查點儲存器(checkpointer)指派給一系列檢查點的唯一識別碼。
在使用檢查點儲存器時,執行圖形時必須指定 thread_id 或 checkpoint_id。

注意:根據 LangGraph v0.2 的官方文件,原先的 thread_ts 和 parent_ts 已分別更名為 checkpoint_id 和 parent_checkpoint_id。

7. 長對話優化技術:過濾與修剪

隨著對話越來越長,歷史記錄會不斷累積,可能會讓 AI 的反應變慢,甚至出錯。怎麼辦呢?我們有兩個好方法:

  1. 過濾消息(Filtering messages): 就像是幫 AI 整理筆記,只保留重要的部分。
  2. 修剪消息(trim messages): 相當於幫 AI 做重點摘要,只保留最新最重要的資訊。

7.1 過濾消息(Filtering messages):提取關鍵信息的技術

在複雜的鏈式結構和代理中,我們可能會使用訊息列表來追蹤狀態。消息過濾技術允許我們從複雜的對話歷史中提取最相關的資訊。通過 LangChain 的 filter_messages 函數,我們可以基於多種條件選擇性地保留或排除特定消息:
官方文件

我們先來看看基礎用法,單純用 LangChain

messages = [
    SystemMessage("你是一個優秀的助理。"),
    HumanMessage("你叫什麼名字?", id="q1", name="台灣使用者"),
    AIMessage("我叫小智。", id="a1", name="AI助理"),
    HumanMessage("你最喜歡的台灣小吃是什麼?", id="q2"),
    AIMessage("我最喜歡的是臺灣的珍珠奶茶!", id="a2"),
]

filtered_msgs = filter_messages(
    messages,
    include_names=("台灣使用者", "AI助理"),
    include_types=("system",),
    exclude_ids=("a1",),
)
print(filtered_msgs)

補上執行圖

這個例子展示了如何過濾訊息,只保留特定的使用者名稱、訊息類型,並排除特定 ID 的訊息。

7.2 修剪消息(trim messages):優化對話長度的方法

管理對話歷史的另一個重要概念是限制傳遞給模型的訊息數量。LangChain 提供了一些內建的輔助函數來管理訊息列表。在這裡,我們將使用 trim_messages 輔助函數來減少傳送給模型的訊息數量。

trim_messages 是 LangChain 提供的一個實用函數,用於管理和限制傳遞給語言模型的訊息數量。其主要目的是確保對話歷史不會超出模型的上下文視窗限制。讓我們深入了解它的運作機制:

trim_messages 的核心思想是根據指定的標準(如最大令牌數)來修剪訊息列表。

官方文件

trimmer = trim_messages(
    max_tokens=100,
    strategy="last",
    token_counter=model,
    include_system=True,  # 保留初始的系統訊息
    allow_partial=False,  # 不允許分割訊息內容
    start_on="human",  # 確保第一條訊息(不包括系統訊息)始終是特定類型
)

補上執行圖

修剪過程如下:

  1. 保留系統訊息:
    由於 include_system=True,首先會保留系統訊息:SystemMessage(content="你是一個了解台灣文化的助理")

  2. 反轉訊息列表:
    因為 strategy="last",訊息列表會被反轉,以便從最新的訊息開始處理。

  3. 開始累加令牌:

  • 首先計算系統訊息的令牌數(假設為 15 個令牌)。
  • 剩餘可用令牌:100 - 15 = 85 個令牌。
  1. 從最新的訊息開始添加:
  • AIMessage(content="當然!台灣有豐富的文化和美食,我很喜歡。") (假設 20 令牌)
  • HumanMessage(content="你喜歡台灣嗎?") (假設 10 令牌)
  • AIMessage(content="不客氣,很高興能幫到你!") (假設 15 令牌)
  • HumanMessage(content="謝謝你的回答") (假設 10 令牌)
    此時已經使用了大約 70 個令牌(15 + 20 + 10 + 15 + 10)。
  1. 達到令牌限制:
  • 下一條訊息 AIMessage(content="不客氣,很高興能幫到你!") 可能會使總令牌數超過 100。
    由於 allow_partial=False,這條訊息不會被部分包含。
  1. 確保以 Human 訊息開始:
    由於 start_on="human",修剪後的列表會確保第一條非系統訊息是 Human 訊息。
    在這個例子中,已經符合要求,不需要額外調整。

在這個例子中,我們使用了與台灣相關的對話內容,並設置了一個修剪器來限制傳送給模型的訊息數量。這種方法可以有效地管理對話歷史,確保不會超出模型的上下文視窗限制,同時保持對話的連貫性和相關性。

8. 結論

在本文中,我們深入探討了如何利用 LangGraph 和 LangChain 的先進功能來增強 AI 助理的記憶能力和對話管理能力。我們介紹了 Checkpointer 機制,它如同 AI 的記憶儲存器,使 AI 能夠在多次互動中保持上下文的連貫性。我們還探討了不同類型的記憶儲存方案,從簡單的 MemorySaver 到複雜的外部數據庫系統,每種方案都有其特定的應用場景。

特別值得注意的是,我們深入研究了如何有效管理對話歷史。通過消息過濾和修剪技術,我們可以精確控制傳遞給 AI 模型的上下文信息,既確保了回應的相關性和準確性,又避免了因上下文過長而導致的性能問題。
這些技術的結合不僅提高了 AI 助理的智能水平,還大大增強了其實用性和效率。通過實施這些方法,我們可以創建出更加智能、更具個性化,且能夠持續學習和適應的 AI 系統。

即刻前往教學程式碼 Repo,親自動手提供 AI 代理額外記憶能力吧!別忘了給專案按個星星並持續關注更新,讓我們一起探索AI代理的新境界。

X. 參考資料

  1. https://langchain-ai.github.io/langgraph/how-tos/memory/manage-conversation-history/
  2. https://langchain-ai.github.io/langgraph/how-tos/persistence/
  3. https://github.com/DhunganaKB/OpenAI-Chat/blob/main/LangGraph/langGraph_memory_turorial_03.ipynb
  4. https://python.langchain.com/v0.2/docs/how_to/filter_messages/
  5. https://python.langchain.com/v0.2/docs/how_to/trim_messages/

上一篇
【Day 18】- LangGraph 與 LangFuse:打造 Agent 觀測系統全方位指南
下一篇
【Day 20】- 結合 LangGraph 與 MongoDB 打造智慧工地安全監控系統:Agentic RAG 技術應用實例
系列文
2024 年用 LangGraph 從零開始實現 Agentic AI System29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言