20-day5-Multi-Session Memory
🧠 Day5-增强版:让 Agent 支持 多会话记忆(Multi-Session Memory)
✅ 本方案在原有单用户记忆基础上,
支持多个独立对话上下文(如 Alice、Bob 各自拥有自己的记忆),
适用于本地多轮测试或未来扩展为 Web 服务。
🔧 一、升级后的程序:agent_with_multi_session.py
tee agent_with_multi_session.py <<'EOF'
# agent_with_multi_session.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
# 加载 .env
load_dotenv()
# 初始化模型(DashScope 兼容 OpenAI)
llm = ChatOpenAI(
model="qwen-max",
openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
temperature=0.7
)
# Prompt 模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有记忆的 AI 助手。请记住用户之前说过的话。"),
MessagesPlaceholder(variable_name="history"), # 👈 ←←←【MEMORY:历史消息插入点】
("human", "{input}")
])
chain = prompt | llm
# 内存存储历史 👈 ←←←【MEMORY 核心:全局字典,支持多 session_id】
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory() # 👈 ←←←【MEMORY:按需创建新会话历史】
return store[session_id]
with_message_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history",
)
if __name__ == "__main__":
print("🤖 多会话记忆型 Agent 启动!")
print("指令:")
print(" - 输入 'quit' 退出")
print(" - 输入 'show_memory' 查看当前会话记忆")
print(" - 输入 'switch <session_id>' 切换到指定会话(如 switch alice)")
print(" - 默认会话 ID: default\n")
current_session = "default"
while True:
try:
user_input = input(f"👤 [{current_session}] 你: ").strip()
except (KeyboardInterrupt, EOFError):
print("\n👋 再见!")
break
if not user_input:
continue
if user_input.lower() == "quit":
break
elif user_input.lower() == "show_memory":
history = get_session_history(current_session)
print("🧠 当前记忆内容:")
if not history.messages:
print(" (无记忆)")
else:
for i, msg in enumerate(history.messages, 1):
role = "👤 用户" if msg.type == "human" else "🤖 Agent"
print(f" {i}. {role}: {msg.content}")
print()
continue
elif user_input.lower().startswith("switch "):
parts = user_input.split(" ", 1)
if len(parts) < 2:
print("⚠️ 用法: switch <session_id>")
continue
new_session = parts[1].strip()
if not new_session:
print("⚠️ 会话 ID 不能为空")
continue
current_session = new_session
print(f"🔄 已切换到会话: '{current_session}'\n")
continue
# 调用带记忆的链,使用当前会话 ID
response = with_message_history.invoke(
{"input": user_input},
config={"configurable": {"session_id": current_session}}
)
print(f"🤖 Agent: {response.content}\n")
EOF
▶️ 二、运行与测试
python agent_with_multi_session.py
🧪 测试流程示例:
👤 [default] 你: switch alice
🔄 已切换到会话: 'alice'
👤 [alice] 你: 我是 Alice,住在杭州。
🤖 Agent: 你好,Alice!很高兴认识你,杭州是个美丽的城市。有什么我可以帮你的吗?
👤 [alice] 你: 我住哪?
🤖 Agent: 你住在杭州。需要我推荐一些杭州的景点或美食吗?
👤 [alice] 你: switch bob
🔄 已切换到会话: 'bob'
👤 [bob] 你: 我是 Bob,来自北京。
🤖 Agent: 你好,Bob!欢迎来到对话。北京有很多有趣的地方,有什么我可以帮忙的吗?
👤 [bob] 你: 我住哪?
🤖 Agent: 你来自北京。
👤 [bob] 你: show_memory
🧠 当前记忆内容:
1. 👤 用户: 我是 Bob,来自北京。
2. 🤖 Agent: 你好,Bob!欢迎来到对话。北京有很多有趣的地方,有什么我可以帮忙的吗?
3. 👤 用户: 我住哪?
4. 🤖 Agent: 你来自北京。
👤 [bob] 你: switch alice
🔄 已切换到会话: 'alice'
👤 [alice] 你: show_memory
🧠 当前记忆内容:
1. 👤 用户: 我是 Alice,住在杭州。
2. 🤖 Agent: 你好,Alice!很高兴认识你,杭州是个美丽的城市。有什么我可以帮你的吗?
3. 👤 用户: 我住哪?
4. 🤖 Agent: 你住在杭州。需要我推荐一些杭州的景点或美食吗?
✅ 验证成功:
- Alice 和 Bob 的记忆完全隔离
- 切换会话后,Agent 只记得当前会话的历史
📚 三、多 Session ID 机制详解
🔍 什么是 session_id?
session_id是一个字符串标识符,用于区分不同的对话上下文。- 在本程序中,它作为
store字典的键,每个键对应一个独立的ChatMessageHistory对象。
🗂️ 数据结构(多会话)
store = {
"alice": ChatMessageHistory(messages=[...]),
"bob": ChatMessageHistory(messages=[...]),
"default": ChatMessageHistory(messages=[...]),
"user_123": ChatMessageHistory(messages=[...]),
# ... 任意数量的独立会话
}
🔄 存储过程(多会话版)
- 用户输入 → 程序知道当前
current_session(如"alice") - 调用:
with_message_history.invoke(..., config={"configurable": {"session_id": "alice"}}) - LangChain:
- 调用
get_session_history("alice") - 从
store["alice"]加载历史 - 调用 LLM
- 将新消息追加到
store["alice"].messages
- 调用
- 其他会话(如
"bob")完全不受影响。
🔎 查询过程(多会话版)
- 自动查询:每次 invoke 时,LangChain 自动读取对应
session_id的历史。 - 手动查询:
show_memory命令读取current_session对应的历史。
🌐 四、多 Session ID 的应用场景
| 场景 | 说明 |
|---|---|
| ✅ 本地多角色测试 | 同一人模拟多个用户(如测试客服系统) |
| ✅ Web 应用 | 每个用户登录后分配唯一 session_id(如用户 ID 或 UUID) |
| ✅ API 服务 | 客户端在请求头中传递 X-Session-ID |
| ✅ 多租户系统 | session_id = f"{tenant_id}_{user_id}" |
💡 关键原则:只要
session_id不同,记忆就完全隔离。
⚠️ 注意事项
- 内存限制:
store是内存字典,长时间运行可能占用大量内存(可定期清理不活跃会话)。 - 持久化需求:若需关机后保留记忆,需将
ChatMessageHistory替换为RedisChatMessageHistory、PostgresChatMessageHistory等。 - 安全考虑:在 Web 服务中,
session_id应由服务端生成并绑定用户身份,避免客户端伪造。
📌 总结
多 Session ID 的核心思想是:用一个字符串标识符(session_id)来隔离不同用户的对话上下文。
- 存储:每条消息按
session_id分类存入store字典- 查询:所有操作都基于当前
session_id进行- 扩展性:天然支持从单用户 → 多用户 → 分布式系统的平滑演进
此设计既保留了原程序的简洁性,又为未来功能扩展打下坚实基础。
浙公网安备 33010602011771号