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=Falseutf-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(可优化为“退出时保存”或“定时保存”)。

🔄 程序运行流程示例

  1. 用户启动程序,默认进入 default 会话。
  2. 输入 "你好" → AI 回复 → 自动保存 memory_dump.json
  3. 输入 switch alice → 切换到新会话。
  4. 输入 "我是 Alice" → AI 回复 → 再次保存,文件包含 defaultalice 两个会话
  5. 关闭程序(quit 或 Ctrl+C)→ 下次启动时,只要重新加载 memory_dump.json 就能恢复记忆注:当前版本未实现“加载”,只实现了“保存”)。

✅ 优势与适用场景

优势 说明
简单直观 无需数据库,一个 JSON 文件搞定
透明可读 直接打开 memory_dump.json 就能看到所有对话
调试友好 可手动编辑、备份、版本控制(如 Git)

📌 总结

agent_with_multi_session_filestore.py 是一个有记忆的 Agent:

  • ✅ 保留了多会话切换能力;
  • 新增了全自动的本地 JSON 持久化机制
  • ✅ 启动时自动加载历史

💾 真正的记忆,不仅要“记得住”,还要“找得回”。

posted @ 2026-01-29 23:20  船山薪火  阅读(14)  评论(0)    收藏  举报
![image](https://img2024.cnblogs.com/blog/3174785/202601/3174785-20260125205854513-941832118.jpg)