บทที่ 6: Memory — การจัดการหน่วยความจำ
ทำไมต้องมี Memory?
Section titled “ทำไมต้องมี Memory?”LLMs ไม่มีหน่วยความจำ ทุกครั้งที่เรียกใช้ จะเริ่มต้นใหม่ทุกครั้ง Memory ช่วยให้ AI “จำ” บทสนทนาก่อนหน้าได้
❌ ไม่มี Memory:User: ผมชื่อสมชายAI: สวัสดีครับคุณสมชาย!User: ผมชื่ออะไร?AI: ขอโทษครับ ผมไม่ทราบชื่อของคุณ ← ลืมแล้ว!
✅ มี Memory:User: ผมชื่อสมชายAI: สวัสดีครับคุณสมชาย!User: ผมชื่ออะไร?AI: คุณชื่อสมชายครับ! ← จำได้!วิธีจัดการ Memory ใน LangChain v1.0+
Section titled “วิธีจัดการ Memory ใน LangChain v1.0+”ใน LangChain เวอร์ชันใหม่ การจัดการ memory ทำผ่าน message history โดยเราเก็บ messages ไว้เองและส่งเข้าไปใน prompt
วิธีพื้นฐาน — จัดการ Messages เอง
Section titled “วิธีพื้นฐาน — จัดการ Messages เอง”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.messages import HumanMessage, AIMessage
llm = ChatOpenAI(model="gpt-4o-mini")
# Prompt ที่มี placeholder สำหรับ chat historyprompt = ChatPromptTemplate.from_messages([ ("system", "คุณเป็นผู้ช่วยที่เป็นมิตร ตอบเป็นภาษาไทย"), MessagesPlaceholder(variable_name="chat_history"), ("human", "{input}"),])
chain = prompt | llm
# เก็บ chat history เองchat_history = []
def chat(user_message: str) -> str: # เรียก LLM พร้อม history response = chain.invoke({ "chat_history": chat_history, "input": user_message, })
# เก็บ messages ลง history chat_history.append(HumanMessage(content=user_message)) chat_history.append(AIMessage(content=response.content))
return response.content
# ทดสอบprint(chat("สวัสดี! ผมชื่อวิชัย"))# สวัสดีครับคุณวิชัย! ยินดีที่ได้รู้จักครับ
print(chat("ผมชื่ออะไร?"))# คุณชื่อวิชัยครับ! 😊RunnableWithMessageHistory
Section titled “RunnableWithMessageHistory”LangChain มี helper สำหรับจัดการ history อัตโนมัติ:
from langchain_core.runnables.history import RunnableWithMessageHistoryfrom langchain_community.chat_message_histories import ChatMessageHistoryfrom langchain_core.chat_history import BaseChatMessageHistory
# เก็บ history แยกตาม sessionstore = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory: if session_id not in store: store[session_id] = ChatMessageHistory() return store[session_id]
# สร้าง chain พร้อม memoryprompt = ChatPromptTemplate.from_messages([ ("system", "คุณเป็นผู้ช่วยที่เป็นมิตร"), MessagesPlaceholder(variable_name="history"), ("human", "{input}"),])
chain = prompt | llm
# Wrap ด้วย RunnableWithMessageHistorychain_with_history = RunnableWithMessageHistory( chain, get_session_history, input_messages_key="input", history_messages_key="history",)
# ใช้งาน — ระบุ session_idconfig = {"configurable": {"session_id": "user-123"}}
response = chain_with_history.invoke( {"input": "สวัสดี! ชื่อมานีค่ะ"}, config=config,)print(response.content)
response = chain_with_history.invoke( {"input": "ฉันชื่ออะไรคะ?"}, config=config,)print(response.content) # จำได้ว่าชื่อมานี!เทคนิค Memory ขั้นสูง
Section titled “เทคนิค Memory ขั้นสูง”1. จำกัดจำนวน Messages (Sliding Window)
Section titled “1. จำกัดจำนวน Messages (Sliding Window)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_core.messages import trim_messagesfrom langchain_core.runnables import RunnablePassthrough
# เก็บแค่ 10 messages ล่าสุดtrimmer = trim_messages( max_tokens=1000, strategy="last", # เก็บ messages ล่าสุด token_counter=llm, # ใช้ LLM นับ tokens include_system=True, # เก็บ system message เสมอ allow_partial=False,)
prompt = ChatPromptTemplate.from_messages([ ("system", "คุณเป็นผู้ช่วยที่เป็นมิตร"), MessagesPlaceholder(variable_name="history"), ("human", "{input}"),])
# ใส่ trimmer ใน chainchain = ( RunnablePassthrough.assign(history=lambda x: trimmer.invoke(x["history"])) | prompt | llm)2. สรุปบทสนทนา (Summary Memory)
Section titled “2. สรุปบทสนทนา (Summary Memory)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_core.prompts import ChatPromptTemplatefrom langchain_openai import ChatOpenAIfrom langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="gpt-4o-mini")
# Chain สำหรับสรุปบทสนทนาsummary_prompt = ChatPromptTemplate.from_template("""สรุปบทสนทนาต่อไปนี้ให้กระชับ เก็บข้อมูลสำคัญไว้:
สรุปก่อนหน้า: {existing_summary}
บทสนทนาใหม่:{new_messages}
สรุปใหม่:""")
summary_chain = summary_prompt | llm | StrOutputParser()
class SummaryMemory: def __init__(self): self.summary = "" self.recent_messages = [] self.max_recent = 6 # เก็บ 6 messages ล่าสุด
def add_message(self, role: str, content: str): self.recent_messages.append(f"{role}: {content}")
# ถ้ามากเกินไป ให้สรุป if len(self.recent_messages) > self.max_recent: self._summarize()
def _summarize(self): # สรุป messages เก่า messages_text = "\n".join(self.recent_messages[:-2]) self.summary = summary_chain.invoke({ "existing_summary": self.summary, "new_messages": messages_text, }) # เก็บแค่ 2 messages ล่าสุด self.recent_messages = self.recent_messages[-2:]
def get_context(self) -> str: context = "" if self.summary: context += f"สรุปบทสนทนาก่อนหน้า: {self.summary}\n\n" if self.recent_messages: context += "บทสนทนาล่าสุด:\n" + "\n".join(self.recent_messages) return context3. Persistent Memory (บันทึกลง Database)
Section titled “3. Persistent Memory (บันทึกลง Database)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
# ใช้ Redis เป็น backendfrom langchain_community.chat_message_histories import RedisChatMessageHistory
def get_session_history(session_id: str): return RedisChatMessageHistory( session_id=session_id, url="redis://localhost:6379", ttl=3600, # เก็บ 1 ชั่วโมง )
# ใช้ SQLite เป็น backendfrom langchain_community.chat_message_histories import SQLChatMessageHistory
def get_session_history(session_id: str): return SQLChatMessageHistory( session_id=session_id, connection="sqlite:///chat_history.db", )ตัวอย่าง: สร้าง Chatbot สมบูรณ์
Section titled “ตัวอย่าง: สร้าง Chatbot สมบูรณ์”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables.history import RunnableWithMessageHistoryfrom langchain_community.chat_message_histories import ChatMessageHistory
# Setupllm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)store = {}
def get_history(session_id: str): if session_id not in store: store[session_id] = ChatMessageHistory() return store[session_id]
# Promptprompt = ChatPromptTemplate.from_messages([ ("system", """คุณเป็น TechBot — ผู้ช่วยด้านเทคโนโลยี - ตอบเป็นภาษาไทย - ให้ตัวอย่างโค้ดเมื่อเหมาะสม - ถามกลับถ้าต้องการข้อมูลเพิ่ม"""), MessagesPlaceholder(variable_name="history"), ("human", "{input}"),])
# Chainchain = prompt | llm | StrOutputParser()chatbot = RunnableWithMessageHistory( chain, get_history, input_messages_key="input", history_messages_key="history",)
# Interactive Chat Loopdef main(): session_id = "demo-session" config = {"configurable": {"session_id": session_id}}
print("🤖 TechBot พร้อมให้บริการ! (พิมพ์ 'quit' เพื่อออก)\n")
while True: user_input = input("คุณ: ") if user_input.lower() == "quit": print("👋 ลาก่อนครับ!") break
response = chatbot.invoke( {"input": user_input}, config=config, ) print(f"🤖 TechBot: {response}\n")
if __name__ == "__main__": main()เปรียบเทียบ Memory Strategies
Section titled “เปรียบเทียบ Memory Strategies”| Strategy | ข้อดี | ข้อเสีย | เหมาะกับ |
|---|---|---|---|
| Full History | จำได้ทั้งหมด | ใช้ tokens มาก | บทสนทนาสั้น |
| Sliding Window | ควบคุม tokens | ลืมเรื่องเก่า | บทสนทนาทั่วไป |
| Summary | ประหยัด tokens | เสีย detail | บทสนทนายาว |
| Persistent | ไม่หายเมื่อ restart | ต้องตั้ง database | Production |