本文 AI 產出,尚未審核

LangChain 教學:Memory - ConversationBufferMemory

簡介

在對話型 AI 應用中,記憶(Memory)是讓模型能夠「保持上下文」的關鍵機制。沒有記憶,ChatGPT 只能根據單一輪提問回應,無法在多輪對話中保持前後關聯,使用者體驗會大打折扣。

LangChain 作為一套用於構建 LLM 應用的框架,提供了多種 Memory 類別,其中最直觀、最常用的就是 ConversationBufferMemory。它會把每一次使用者的輸入與模型的回覆都串接成一段文字,形成「對話緩衝區」,讓後續的 Prompt 能自動攜帶完整的對話歷史。

本篇文章將從概念說明、程式碼範例、常見陷阱與最佳實踐,最後列出實務應用場景,帶你一步步掌握 ConversationBufferMemory 的使用方式,適合剛入門到已有一定開發經驗的讀者。


核心概念

1. ConversationBufferMemory 是什麼?

ConversationBufferMemory 簡單來說就是一個 文字緩衝,它會把每一次的 human(使用者)與 ai(模型)訊息依序累加,形成類似:

Human: 你好
AI: 你好!有什麼可以幫忙的嗎?
Human: 請介紹一下台北101
AI: 台北101是...

在每次呼叫 LLM 時,框架會自動把這段緩衝文字插入 Prompt,讓模型知道「先前已說過什麼」。

2. 為什麼選擇 Buffer 而不是其他 Memory?

  • 簡單直觀:不需要自行設計資料結構,框架自動管理。
  • 適合短對話:對於對話輪數在 10~20 輪以內的情境,效能與成本都相當友好。
  • 易於除錯:緩衝內容直接以文字呈現,開發者可以輕鬆印出檢查。

若對話過長或需要更細粒度的記憶(例如只記住關鍵資訊),可以考慮 ConversationSummaryMemoryVectorStoreRetrieverMemory 等進階方案。

3. 主要屬性與方法

屬性/方法 說明
memoryKey 在 Prompt 中使用的變數名稱,預設為 "history"
returnMessages 設為 true 時,返回 Message 物件陣列;false(預設)則返回純文字。
clear() 清空緩衝區,常用於重新開始新對話。
loadMemoryVariables(values) 取得目前的緩衝內容,供 Prompt 使用。

程式碼範例

以下範例採用 LangChainJS(JavaScript / TypeScript)撰寫,請先安裝相關套件:

npm install langchain openai

:本教學使用 OpenAI 的 gpt-3.5-turbo,請自行設定 OPENAI_API_KEY 環境變數。

範例 1:最簡單的 ConversationBufferMemory

import { OpenAI } from "langchain/llms/openai";
import { ConversationChain } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";

// 建立 LLM
const llm = new OpenAI({ temperature: 0 });

// 建立 Memory
const memory = new ConversationBufferMemory();

// 建立 Chain,將 LLM 與 Memory 綁定
const chain = new ConversationChain({ llm, memory });

async function chat() {
  console.log(await chain.call({ input: "你好!" }));
  console.log(await chain.call({ input: "今天台北天氣如何?" }));
}
chat();

說明

  • ConversationChain 會自動把 memory 中的 history 文字插入 Prompt。
  • 每次 chain.call 後,Memory 都會自動更新緩衝區。

範例 2:自訂 memoryKeyreturnMessages

import { OpenAI } from "langchain/llms/openai";
import { ConversationChain } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";

const llm = new OpenAI({ temperature: 0 });

const memory = new ConversationBufferMemory({
  memoryKey: "chat_history",   // 在 Prompt 中使用的變數名稱
  returnMessages: true        // 回傳 Message 物件,方便後續處理
});

const chain = new ConversationChain({ llm, memory });

async function chat() {
  const res1 = await chain.call({ input: "請告訴我什麼是區塊鏈?" });
  console.log(res1.response);

  // 直接取得緩衝的 Message 陣列
  const messages = await memory.loadMemoryVariables({});
  console.log("緩衝內容(Message 物件):", messages.chat_history);
}
chat();

說明

  • 設定 memoryKeychat_history,在 Prompt 中會變成 {{chat_history}}
  • returnMessages: true 讓我們可以取得更結構化的訊息(含 role、content),適合要做訊息過濾或摘要的情況。

範例 3:在自訂 Prompt 中使用 ConversationBufferMemory

import { OpenAI } from "langchain/llms/openai";
import { LLMChain, PromptTemplate } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";

const llm = new OpenAI({ temperature: 0 });

const prompt = PromptTemplate.fromTemplate(
  `以下是一段對話歷史,請根據內容回答使用者的問題。
  
{history}
  
使用者: {input}
AI:`
);

const memory = new ConversationBufferMemory({});

const chain = new LLMChain({ llm, prompt, memory });

async function chat() {
  console.log(await chain.call({ input: "什麼是深度學習?" }));
  console.log(await chain.call({ input: "它跟機器學習有什麼差別?" }));
}
chat();

說明

  • 這裡使用 LLMChain 搭配自訂 PromptTemplate,在 Prompt 中手動插入 {history}(即 memoryKey 的預設值)。
  • 這種寫法適合想要在 Prompt 中加入額外說明或格式化的情況。

範例 4:清除記憶並重新開始對話

import { OpenAI } from "langchain/llms/openai";
import { ConversationChain } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";

const llm = new OpenAI({ temperature: 0 });
const memory = new ConversationBufferMemory();
const chain = new ConversationChain({ llm, memory });

async function chat() {
  await chain.call({ input: "先說個笑話。" });
  console.log("第一輪結束,緩衝內容:", await memory.loadMemoryVariables({}));

  // 清除記憶
  memory.clear();

  console.log("已清除緩衝,重新開始:");
  await chain.call({ input: "現在請告訴我 Python 的基礎語法。" });
}
chat();

說明

  • memory.clear() 會把緩衝區全部清空,適合在使用者要求「重新開始」或切換主題時使用。

範例 5:結合檔案儲存持久化(簡易版)

import fs from "fs";
import { OpenAI } from "langchain/llms/openai";
import { ConversationChain } from "langchain/chains";
import { ConversationBufferMemory } from "langchain/memory";

const llm = new OpenAI({ temperature: 0 });
const memoryFile = "./chat_memory.json";

// 從檔案載入過去的緩衝(若不存在則建立空緩衝)
let initHistory = "";
if (fs.existsSync(memoryFile)) {
  initHistory = fs.readFileSync(memoryFile, "utf-8");
}
const memory = new ConversationBufferMemory({ memoryKey: "history" });
memory.chatHistory = initHistory; // 手動設定初始內容

const chain = new ConversationChain({ llm, memory });

async function chat() {
  const res = await chain.call({ input: "今天的新聞有什麼值得關注的?" });
  console.log(res.response);

  // 把最新的緩衝寫回檔案
  const { history } = await memory.loadMemoryVariables({});
  fs.writeFileSync(memoryFile, history);
}
chat();

說明

  • 這個範例示範如何把緩衝內容寫入本地檔案,讓聊天紀錄在程式重啟後仍能保留。
  • 在正式產品中,建議改用資料庫或雲端儲存,以確保安全與擴充性。

常見陷阱與最佳實踐

陷阱 可能的後果 解決方案或最佳實踐
緩衝過長 Prompt 超過 LLM 的 token 限制,導致錯誤或成本激增。 - 設定 maxTokenLimit(自行截斷)
- 定期使用 ConversationSummaryMemory 產生摘要
- 只保留最近 N 輪對話
忘記清除記憶 使用者切換話題時仍會帶入舊有上下文,產生不相關回覆。 - 在 UI 中提供「重新開始」按鈕,呼叫 memory.clear()
- 根據關鍵字或意圖自動清除
returnMessages 與 Prompt 不匹配 若 Prompt 期待純文字但 returnMessages:true,會出現型別錯誤。 - 確認 memoryKey 與 Prompt 中的變數型別一致
- 若使用 Message 陣列,需自行組合成文字
多使用者環境共用同一 Memory 實例 不同使用者的對話互相污染,隱私風險。 - 為每位使用者建立獨立的 Memory 物件(或使用 Session ID 做映射)
未處理 API 錯誤 網路或金鑰失效時,整個對話流程卡住。 - 包裝 chain.calltry/catch,並回傳友善錯誤訊息

小技巧

  1. 限制緩衝字元數
    const MAX_CHAR = 2000;
    const trimmed = history.slice(-MAX_CHAR);
    memory.chatHistory = trimmed;
    
  2. 結合摘要:在第 N 輪自動把過往對話摘要後重寫緩衝,降低 token 數。
  3. 使用 Session ID:在多使用者聊天室中,將 memory 放入 Map<sessionId, Memory>,確保隔離。

實際應用場景

場景 為何選 ConversationBufferMemory 示範功能
客服聊天機器人 客服對話往往在 10~15 輪內,完整保留上下文能提升解答正確率。 使用者提問「訂單編號 1234 為何未出貨?」 → 機器人根據前一次的「請提供訂單編號」回覆自動帶入。
教育輔助教練 教學對話需要累積學生的提問與教師的回覆,以便後續檢視學習進度。 學生問「什麼是二次函數?」 → 教練回覆後,後續問題「請舉例說明」仍能參考先前的解釋。
旅遊行程規劃 使用者會分步驟提供需求(日期、景點、預算),需要把每一步的資訊串接起來。 第一步:「我想去東京 3 天」 → 第二步:「預算 2 萬」 → 第三步:「請安排住宿」
程式除錯助手 開發者在多輪對話中提供錯誤訊息與程式碼片段,模型需要持續追蹤上下文。 開發者:「這段程式拋出 TypeError」 → 模型回問「是哪一行?」 → 記憶保持前後訊息,最終給出修正建議。
個人助理(日程管理) 使用者會先說「提醒我明天 9 點開會」再說「請把會議議程加入」——需要把時間與議程串在一起。 透過緩衝,助理能一次性產出完整的日程條目。

總結

  • ConversationBufferMemory 是 LangChain 中最易上手、最直觀的記憶機制,適合大多數短對話應用。
  • 它的核心概念是把每一次的 HumanAI 訊息串接成文字緩衝,並在每次呼叫 LLM 時自動注入 Prompt。
  • 使用時要注意 緩衝長度多使用者隔離、以及 何時清除,以避免 token 超限或上下文污染。
  • 透過簡單的 API(memoryKeyreturnMessagesclear())即可客製化行為,配合自訂 Prompt、摘要或持久化儲存,可打造從客服機器人到程式除錯助手的完整對話系統。

掌握好 ConversationBufferMemory,你就能在開發 LLM 應用時,快速為模型提供「記憶」這個關鍵能力,提升使用者體驗與系統效能。祝你玩得開心、開發順利! 🚀