21-day5-多任务+文件存储 agent_with_multi_session_filestore
程序 1:多任务+文件存储
agent_with_multi_session_filestore
tee agent_with_multi_session_filestore.py <<'EOF'
# agent_with_multi_session_filestore.py
import os
import json
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
from langchain_core.messages import HumanMessage, AIMessage # 👈 提前导入,避免循环导入风险
# 加载 .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"), # 👈 历史消息插入点
("human", "{input}")
])
chain = prompt | llm
# 内存存储历史:全局字典,支持多 session_id
store = {}
def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
with_message_history = RunnableWithMessageHistory(
chain,
get_session_history,
input_messages_key="input",
history_messages_key="history",
)
# ==============================
# 🔽 持久化支持函数
# ==============================
def serialize_store():
"""将内存中的 store 转换为可 JSON 序列化的字典。"""
return {
session_id: [
{"type": msg.type, "content": msg.content}
for msg in history.messages
]
for session_id, history in store.items()
}
def save_store_to_file(filename="memory_dump.json"):
"""将当前所有会话记忆保存到本地 JSON 文件。"""
try:
with open(filename, "w", encoding="utf-8") as f:
json.dump(serialize_store(), f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"⚠️ 保存记忆失败: {e}")
def load_store_from_file(filename="memory_dump.json"):
"""启动时从 JSON 文件加载所有会话记忆到内存 store。"""
global store
try:
with open(filename, "r", encoding="utf-8") as f:
data = json.load(f)
# 清空当前 store(防止与残留数据冲突)
store.clear()
for session_id, messages in data.items():
history = ChatMessageHistory()
for msg in messages:
# 根据 type 重建 LangChain 消息对象
if msg["type"] == "human":
history.add_message(HumanMessage(content=msg["content"]))
elif msg["type"] == "ai":
history.add_message(AIMessage(content=msg["content"]))
else:
# 忽略未知类型(或可记录警告)
continue
store[session_id] = history
print(f"✅ 已从 {filename} 加载 {len(store)} 个会话记忆。")
except FileNotFoundError:
print("📁 未找到记忆文件,初始化空记忆。")
except json.JSONDecodeError:
print(f"⚠️ {filename} 格式错误,无法加载记忆。")
except Exception as e:
print(f"⚠️ 加载记忆失败: {e}")
# ==============================
# 主程序入口
# ==============================
if __name__ == "__main__":
# 👇 启动时自动加载记忆
load_store_from_file()
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
# 调用带记忆的链
response = with_message_history.invoke(
{"input": user_input},
config={"configurable": {"session_id": current_session}}
)
print(f"🤖 Agent: {response.content}\n")
# 每次对话后自动保存
save_store_to_file()
EOF
程序 2:view_memory.py 查看内存快照
tee view_memory.py <<'EOF'
#!/usr/bin/env python3
# view_memory.py - 查看 agent 的记忆快照
import json
import sys
import os
def main():
filename = "memory_dump.json"
if not os.path.exists(filename):
print(f"❌ 文件 {filename} 不存在。请先运行主程序生成记忆。")
return
try:
with open(filename, "r", encoding="utf-8") as f:
store_data = json.load(f)
except Exception as e:
print(f"⚠️ 读取失败: {e}")
return
if not store_data:
print("🧠 当前无任何会话记忆。")
return
print("📊 所有会话记忆内容:")
for session_id, messages in store_data.items():
print(f"\n📁 会话 ID: '{session_id}'")
if not messages:
print(" (空)")
else:
for i, msg in enumerate(messages, 1):
role = "👤 用户" if msg["type"] == "human" else "🤖 Agent"
print(f" {i}. {role}: {msg['content']}")
if __name__ == "__main__":
main()
EOF
🎯 程序目标
构建一个 支持多个独立用户会话 的 AI 助手,并且 每次对话后自动将所有会话历史保存到本地 JSON 文件,实现断电不丢记忆。
🔑 核心特性
| 特性 | 说明 |
|---|---|
| ✅ 多会话隔离 | 每个 session_id(如 alice, bob)拥有独立对话历史 |
| ✅ 实时交互 | 支持 switch 切换会话、show_memory 查看当前记忆 |
| ✅ 自动持久化 | 每次用户输入并得到回复后,自动将全部会话写入 memory_dump.json |
| ✅ 中文友好 | 使用 ensure_ascii=False 和 utf-8 编码,正确保存中文 |
🧱 核心组件解析
1. 内存存储:store 字典
store = {}
- 仍然是一个全局字典,键为
session_id,值为ChatMessageHistory对象。 - 所有对话首先在内存中操作(高效、低延迟)。
2. 会话获取函数:get_session_history
def get_session_history(session_id: str):
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
- 与之前一致,按需创建新会话。
3. 新增:序列化与保存函数
▶ serialize_store()
def serialize_store():
return {
session_id: [
{"type": msg.type, "content": msg.content}
for msg in history.messages
]
for session_id, history in store.items()
}
- 将
store中每个ChatMessageHistory对象转换为纯 Python 字典列表。 - 输出格式示例:
{ "default": [ {"type": "human", "content": "你好"}, {"type": "ai", "content": "你好!"} ], "alice": [ {"type": "human", "content": "我叫 Alice"}, {"type": "ai", "content": "很高兴认识你,Alice!"} ] }
▶ save_store_to_file(filename="memory_dump.json")
with open(filename, "w", encoding="utf-8") as f:
json.dump(serialize_store(), f, ensure_ascii=False, indent=2)
- 将序列化结果写入 JSON 文件。
ensure_ascii=False→ 保留中文字符(否则会变成\u4f60\u597d)。indent=2→ 格式化输出,便于人工查看或调试。
4. 关键增强:每次对话后自动保存
# 在主循环中,每次收到 response 后:
save_store_to_file()
- 这是最核心的持久化逻辑。
- 虽然简单粗暴(每次都全量写入),但对小型应用足够可靠。
- 优点:实现简单、不易出错、数据始终最新。
- 缺点:频繁 I/O(可优化为“退出时保存”或“定时保存”)。
🔄 程序运行流程示例
- 用户启动程序,默认进入
default会话。 - 输入
"你好"→ AI 回复 → 自动保存memory_dump.json。 - 输入
switch alice→ 切换到新会话。 - 输入
"我是 Alice"→ AI 回复 → 再次保存,文件包含default和alice两个会话。 - 关闭程序(
quit或 Ctrl+C)→ 下次启动时,只要重新加载memory_dump.json就能恢复记忆(注:当前版本未实现“加载”,只实现了“保存”)。
✅ 优势与适用场景
| 优势 | 说明 |
|---|---|
| 简单直观 | 无需数据库,一个 JSON 文件搞定 |
| 透明可读 | 直接打开 memory_dump.json 就能看到所有对话 |
| 调试友好 | 可手动编辑、备份、版本控制(如 Git) |
📌 总结
agent_with_multi_session_filestore.py 是一个有记忆的 Agent:
- ✅ 保留了多会话切换能力;
- ✅ 新增了全自动的本地 JSON 持久化机制;
- ✅ 启动时自动加载历史
💾 真正的记忆,不仅要“记得住”,还要“找得回”。
浙公网安备 33010602011771号