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=[...]),
    # ... 任意数量的独立会话
}

🔄 存储过程(多会话版)

  1. 用户输入 → 程序知道当前 current_session(如 "alice"
  2. 调用:
    with_message_history.invoke(..., config={"configurable": {"session_id": "alice"}})
    
  3. LangChain:
    • 调用 get_session_history("alice")
    • store["alice"] 加载历史
    • 调用 LLM
    • 将新消息追加到 store["alice"].messages
  4. 其他会话(如 "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 不同,记忆就完全隔离。


⚠️ 注意事项

  1. 内存限制store 是内存字典,长时间运行可能占用大量内存(可定期清理不活跃会话)。
  2. 持久化需求:若需关机后保留记忆,需将 ChatMessageHistory 替换为 RedisChatMessageHistoryPostgresChatMessageHistory 等。
  3. 安全考虑:在 Web 服务中,session_id 应由服务端生成并绑定用户身份,避免客户端伪造。

📌 总结

多 Session ID 的核心思想是:用一个字符串标识符(session_id)来隔离不同用户的对话上下文。

  • 存储:每条消息按 session_id 分类存入 store 字典
  • 查询:所有操作都基于当前 session_id 进行
  • 扩展性:天然支持从单用户 → 多用户 → 分布式系统的平滑演进

此设计既保留了原程序的简洁性,又为未来功能扩展打下坚实基础。

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