Day 6 of LangChain Series: Using Memory and Chains to Make AI Remember Context
This article explains why LLMs need memory, compares BufferMemory and ConversationSummaryBufferMemory, demonstrates how to build memory‑aware chains with LangChain Expression Language (LCEL), and shows practical examples including translation, RAG, and callbacks for monitoring chain execution.
Why Memory Is Needed
Without memory, each LLM interaction is isolated and the model cannot recall previous dialogue; with memory, the model can retain key information across turns to achieve true conversational continuity.
Memory Types Comparison
BufferMemory – stores raw dialogue, lightweight, suitable for simple scenarios, but incurs high token consumption.
ConversationSummaryBufferMemory – automatically summarizes when token limits are exceeded, ideal for long conversations, though its implementation is more complex.
2.1 BufferMemory: Raw Dialogue Storage
from langchain_classic.memory import ConversationBufferMemory
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5", temperature=0)
memory = ConversationBufferMemory(return_messages=True)
# Add dialogue history
memory.save_context({"input": "我叫张三,在字节工作"}, {"output": "你好张三,很高兴认识你!"})
memory.save_context({"input": "我想学 Python"}, {"output": "推荐从《Python 编程:从入门到实践》开始"})
history = memory.load_memory_variables({})["history"]
history_text = "
".join([f"{m.type}: {m.content}" for m in history])
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个对话助手,以下是对话历史:
{history}"),
("human", "{question}")
])
chain = prompt | llm | StrOutputParser()
response = chain.invoke({"history": history_text, "question": "你还记得我叫什么名字吗?"})
print(response) # 输出:记得,你叫张三,在字节工作2.2 ConversationSummaryBufferMemory: Automatic Summarization
from langchain_classic.memory import ConversationSummaryBufferMemory
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5", temperature=0)
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=100) # 超过 100 tokens 自动摘要
memory.save_context({"input": "我叫李四,是一名后端工程师"}, {"output": "你好李四!后端工程师,用 Python 还是 Go 比较多?"})
memory.save_context({"input": "主要是 Python,用 FastAPI 做 REST API"}, {"output": "FastAPI 很适合快速构建高性能 API,你们的架构是什么样的?"})
summary = memory.load_memory_variables({})
print(summary["history"]) # 输出:李四是一名后端工程师,主要使用 Python 和 FastAPI 开发 REST API2.3 ConversationSummaryBufferMemory: Mixed Mode
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=1000) # 超过 1000 token 自动摘要
for i in range(10):
memory.save_context({"input": f"这是第{i+1}轮对话"}, {"output": f"我已经记住了这是第{i+1}轮对话"})
summary = memory.load_memory_variables({})
print(summary["history"]) # 超过 1000 token 时,旧的对话会被压缩成摘要LCEL: Composing Chains Like Building Blocks
3.1 What Is LCEL
LCEL (LangChain Expression Language) uses the | pipeline operator to combine Runnable objects.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5")
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个{role}助手"),
("human", "{question}")
])
parser = StrOutputParser()
chain = prompt | llm | parser
result = chain.invoke({"role": "技术文档", "question": "解释什么是 FastAPI"})3.2 LCEL Supported Operations
from langchain_core.runnables import RunnableParallel, RunnableLambda
# Parallel execution of multiple tasks
parallel_chain = RunnableParallel(
summary=summarize_chain,
translate=translate_chain,
extract_keywords=keywords_chain
)
result = parallel_chain.invoke({"text": "LangChain is powerful"})
# Returns: {"summary": "...", "translate": "...", "extract_keywords": [...]}
# Custom function
def filter_short(items: list) -> list:
return [i for i in items if len(i) < 10]
chain = previous_chain | RunnableLambda(filter_short)3.3 Common LCEL Patterns
Basic Chain : prompt | llm | parser – suitable for simple tasks.
With Memory : memory + LCEL – enables multi‑turn dialogue.
RAG : retriever | chain – question‑answer retrieval.
Parallel : RunnableParallel(a=..., b=...) – concurrent execution of multiple subtasks.
Practical: Custom Chain with Memory
4.1 Memory‑Enabled Translation Chain
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_classic.memory import ConversationSummaryBufferMemory
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5", temperature=0)
translate_memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=500)
TRANSLATE_PROMPT = ChatPromptTemplate.from_messages([
("system", "你是一个专业的翻译助手,将{source_lang}翻译成{target_lang}。
对话历史:
{history}"),
("human", "{text}")
])
translate_chain = TRANSLATE_PROMPT | llm | StrOutputParser()
history = translate_memory.load_memory_variables({})["history"]
history_text = "
".join([f"{m.type}: {m.content}" for m in history]) if history else ""
result = translate_chain.invoke({
"source_lang": "中文",
"target_lang": "日语",
"text": "大模型正在改变软件开发行业",
"history": history_text
})
print(result) # 输出:LLMはソフトウェア開発業界を変革しつつあります
translate_memory.save_context({"input": "翻译:大模型正在改变软件开发行业"}, {"output": result})4.2 RAG + Memory Combination
from operator import itemgetter
from langchain_openai import ChatOpenAI
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_classic.memory import ConversationSummaryBufferMemory
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1. Load vector store
vectorstore = Chroma(persist_directory="./company_kb", embedding_function=OpenAIEmbeddings(model="text-embedding-3-small"))
# 2. Create LLM and retriever
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5", temperature=0)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
# 3. Conversation memory
memory = ConversationSummaryBufferMemory(llm=llm, max_token_limit=1000, return_messages=True)
# 4. Build RAG chain using LCEL
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的问答助手,基于参考文档回答问题。
参考文档:
{context}
对话历史:
{history}"),
("human", "{question}")
])
rag_chain = (
{
"context": itemgetter("question") | retriever | (lambda docs: "
".join(d.page_content for d in docs)),
"history": itemgetter("history"),
"question": itemgetter("question")
}
| prompt
| llm
| StrOutputParser()
)
def chat(question: str):
history = memory.load_memory_variables({})["history"]
history_text = "
".join([f"{m.type}: {m.content}" for m in history]) if history else ""
result = rag_chain.invoke({"question": question, "history": history_text})
memory.save_context({"input": question}, {"output": result})
return result
print(chat("公司有什么培训机会?"))
print(chat("这些培训收费吗?")) # AI 知道你在问培训的收费问题Callbacks: Monitoring Chain Execution
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.prompts import ChatPromptTemplate
class DebugHandler(BaseCallbackHandler):
"""Custom callback that prints each step's input and output"""
def on_chain_start(self, serialized, inputs, **kwargs):
print(f"🔵 Chain 开始:{serialized.get('name', 'unknown')}")
print(f" 输入:{inputs}")
def on_chain_end(self, outputs, **kwargs):
print("🟢 Chain 结束")
print(f" 输出:{outputs}")
def on_llm_start(self, serialized, prompts, **kwargs):
print("🔵 LLM 调用开始")
def on_llm_end(self, response, **kwargs):
print("🟢 LLM 调用结束")
print(f" Token 消耗:{response.llm_output.get('token_usage', {})}")
llm = ChatOpenAI(model="Pro/MiniMaxAI/MiniMax-M2.5")
prompt = ChatPromptTemplate.from_messages([("human", "{text}")])
chain = prompt | llm
result = chain.invoke({"text": "你好"}, config={"callbacks": [DebugHandler()]})Key Takeaways :
BufferMemory stores raw dialogue; ConversationSummaryBufferMemory provides automatic summarization for long chats.
LCEL’s | operator enables concise, modular chain composition.
Parallel execution, custom lambdas, and callbacks extend flexibility and observability.
Combining memory with RAG or translation chains demonstrates real‑world multi‑turn applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tech Ocean
Focused on AI programming, sharing ready-to-use development efficiency solutions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
