LangBot会话管理和上下文保持

摘要

会话管理和上下文保持是构建智能聊天机器人的关键技术之一。LangBot通过其会话管理器(SessionManager)和相关组件,提供了强大的会话管理和上下文保持功能,使得机器人能够在多轮对话中保持连贯性和智能性。本文将深入解析LangBot的会话管理机制,包括会话的创建、维护、持久化以及上下文信息的管理,帮助开发者更好地理解和使用这一重要功能。

正文

1. 会话管理概述

在聊天机器人系统中,会话(Session)是指用户与机器人之间的一次完整交互过程。LangBot的会话管理系统具有以下特点:

  • 多维度识别:支持按用户、群组等不同维度识别会话
  • 上下文保持:在多轮对话中保持上下文信息
  • 状态管理:管理会话的状态和生命周期
  • 并发控制:控制同一会话的并发访问
  • 持久化存储:将会话数据持久化存储,支持服务重启后恢复

2. 系统架构

LangBot会话管理系统的架构如下图所示:

外部依赖
会话管理系统
流水线控制器
大语言模型
插件系统
会话管理器
会话存储
会话对象
消息历史
会话状态
上下文变量

3. 核心组件

3.1 会话管理器(SessionManager)

会话管理器是会话系统的核心组件,负责会话的创建、获取和管理:

class SessionManager:
    """会话管理器"""
    
    def __init__(self, ap: app.Application):
        self.ap = ap
        self.sessions: dict[str, Session] = {}
    
    async def get_session(self, query: Query) -> Session:
        """
        获取或创建会话
        
        Args:
            query: 查询对象
            
        Returns:
            会话对象
        """
        # 根据查询信息生成会话键
        session_key = self._generate_session_key(query)
        
        # 如果会话不存在,创建新会话
        if session_key not in self.sessions:
            session = Session(
                launcher_type=query.launcher_type,
                launcher_id=query.launcher_id,
                sender_id=query.sender_id
            )
            self.sessions[session_key] = session
            
            # 从持久化存储加载会话数据
            await self._load_session_from_storage(session)
        
        return self.sessions[session_key]
    
    def _generate_session_key(self, query: Query) -> str:
        """
        生成会话键
        
        Args:
            query: 查询对象
            
        Returns:
            会话键
        """
        # 对于个人会话,使用发送者ID作为键
        if query.launcher_type == LauncherTypes.PERSON:
            return f"person:{query.sender_id}"
        # 对于群组会话,使用群组ID作为键
        elif query.launcher_type == LauncherTypes.GROUP:
            return f"group:{query.launcher_id}"
        else:
            # 默认使用发送者ID
            return f"default:{query.sender_id}"
    
    async def _load_session_from_storage(self, session: Session):
        """
        从存储中加载会话数据
        
        Args:
            session: 会话对象
        """
        # 从数据库加载会话数据
        result = await self.ap.persistence_mgr.execute_async(
            sqlalchemy.select(persistence_session.SessionData)
            .where(persistence_session.SessionData.session_key == session.session_key)
        )
        
        session_data = result.first()
        if session_data:
            # 恢复消息历史
            session.messages = session_data.messages
            # 恢复上下文变量
            session.variables = session_data.variables
            # 恢复会话状态
            session.state = session_data.state
3.2 会话对象(Session)

会话对象封装了会话的所有信息:

class Session:
    """会话对象"""
    
    def __init__(
        self, 
        launcher_type: LauncherTypes,
        launcher_id: str,
        sender_id: str
    ):
        self.launcher_type = launcher_type
        self.launcher_id = launcher_id
        self.sender_id = sender_id
        self.session_key = self._generate_session_key()
        
        # 消息历史
        self.messages: list[dict] = []
        
        # 上下文变量
        self.variables: dict = {}
        
        # 会话状态
        self.state: dict = {}
        
        # 会话信号量,用于并发控制
        self._semaphore = asyncio.Semaphore(1)
        
        # 会话创建时间
        self.created_at = datetime.now()
        
        # 会话最后活动时间
        self.last_activity = datetime.now()
    
    def _generate_session_key(self) -> str:
        """生成会话键"""
        if self.launcher_type == LauncherTypes.PERSON:
            return f"person:{self.sender_id}"
        elif self.launcher_type == LauncherTypes.GROUP:
            return f"group:{self.launcher_id}"
        else:
            return f"default:{self.sender_id}"
    
    def append_message(self, message: dict):
        """
        添加消息到历史记录
        
        Args:
            message: 消息对象
        """
        self.messages.append(message)
        self.last_activity = datetime.now()
        
        # 限制消息历史长度
        if len(self.messages) > 50:  # 最多保留50条消息
            self.messages = self.messages[-50:]
    
    def get_context(self) -> dict:
        """
        获取会话上下文
        
        Returns:
            会话上下文
        """
        return {
            "messages": self.messages.copy(),
            "variables": self.variables.copy(),
            "state": self.state.copy(),
            "launcher_type": self.launcher_type,
            "launcher_id": self.launcher_id,
            "sender_id": self.sender_id
        }
    
    def set_variable(self, key: str, value: Any):
        """
        设置上下文变量
        
        Args:
            key: 变量名
            value: 变量值
        """
        self.variables[key] = value
        self.last_activity = datetime.now()
    
    def get_variable(self, key: str, default: Any = None) -> Any:
        """
        获取上下文变量
        
        Args:
            key: 变量名
            default: 默认值
            
        Returns:
            变量值
        """
        return self.variables.get(key, default)
    
    async def save(self, persistence_mgr: PersistenceManager):
        """
        保存会话到持久化存储
        
        Args:
            persistence_mgr: 持久化管理器
        """
        # 更新最后活动时间
        self.last_activity = datetime.now()
        
        # 保存到数据库
        await persistence_mgr.execute_async(
            sqlalchemy.insert(persistence_session.SessionData)
            .values(
                session_key=self.session_key,
                launcher_type=self.launcher_type.value,
                launcher_id=self.launcher_id,
                sender_id=self.sender_id,
                messages=self.messages,
                variables=self.variables,
                state=self.state,
                created_at=self.created_at,
                last_activity=self.last_activity
            )
            .on_conflict_do_update(
                index_elements=['session_key'],
                set_=dict(
                    messages=self.messages,
                    variables=self.variables,
                    state=self.state,
                    last_activity=self.last_activity
                )
            )
        )

4. 上下文保持机制

LangBot通过多种机制实现上下文保持:

4.1 消息历史保持
class MessageHistoryKeeper:
    """消息历史保持器"""
    
    def __init__(self, max_history: int = 10):
        self.max_history = max_history
        self.history: list[dict] = []
    
    def add_message(self, message: dict):
        """
        添加消息到历史
        
        Args:
            message: 消息对象
        """
        self.history.append(message)
        
        # 保持历史长度在限制范围内
        if len(self.history) > self.max_history:
            self.history = self.history[-self.max_history:]
    
    def get_history(self) -> list[dict]:
        """
        获取消息历史
        
        Returns:
            消息历史列表
        """
        return self.history.copy()
    
    def clear_history(self):
        """清空消息历史"""
        self.history.clear()
4.2 上下文变量管理
class ContextVariableManager:
    """上下文变量管理器"""
    
    def __init__(self):
        self.variables: dict = {}
    
    def set_variable(self, key: str, value: Any):
        """
        设置变量
        
        Args:
            key: 变量名
            value: 变量值
        """
        self.variables[key] = value
    
    def get_variable(self, key: str, default: Any = None) -> Any:
        """
        获取变量
        
        Args:
            key: 变量名
            default: 默认值
            
        Returns:
            变量值
        """
        return self.variables.get(key, default)
    
    def delete_variable(self, key: str):
        """
        删除变量
        
        Args:
            key: 变量名
        """
        if key in self.variables:
            del self.variables[key]
    
    def get_all_variables(self) -> dict:
        """
        获取所有变量
        
        Returns:
            所有变量的副本
        """
        return self.variables.copy()

5. 在流水线中使用会话

在LangBot的流水线中,会话管理被广泛应用:

5.1 会话获取阶段
@stage.stage_class("session-init")
class SessionInitStage(stage.PipelineStage):
    """会话初始化阶段"""
    
    async def process(
        self,
        query: pipeline_query.Query,
        stage_inst_name: str,
    ) -> entities.StageProcessResult:
        """处理消息"""
        # 获取或创建会话
        session = await self.ap.sess_mgr.get_session(query)
        
        # 将会话关联到查询对象
        query.session = session
        
        # 将用户消息添加到会话历史
        user_message = {
            "role": "user",
            "content": query.message_chain.get_text(),
            "timestamp": datetime.now().isoformat()
        }
        session.append_message(user_message)
        
        # 将会话上下文变量复制到查询变量中
        query.variables.update(session.variables)
        
        return entities.StageProcessResult(
            result_type=entities.ResultType.CONTINUE,
            new_query=query,
            console_notice=f"会话初始化完成,会话键: {session.session_key}"
        )
5.2 上下文使用阶段
@stage.stage_class("context-aware-process")
class ContextAwareProcessStage(stage.PipelineStage):
    """上下文感知处理阶段"""
    
    async def process(
        self,
        query: pipeline_query.Query,
        stage_inst_name: str,
    ) -> entities.StageProcessResult:
        """处理消息"""
        session = query.session
        
        # 获取会话上下文
        context = session.get_context()
        
        # 获取历史消息
        history = context["messages"]
        
        # 获取上下文变量
        user_name = session.get_variable("user_name", "用户")
        user_preferences = session.get_variable("preferences", {})
        
        # 构造包含上下文的提示词
        prompt = self._build_contextual_prompt(
            query.message_chain.get_text(),
            history,
            user_name,
            user_preferences
        )
        
        # 调用大语言模型
        default_model = await self.ap.model_mgr.get_default_model()
        response = await default_model.requester.invoke_llm(
            query=query,
            model=default_model,
            messages=[{"role": "user", "content": prompt}]
        )
        
        # 将模型回复添加到会话历史
        assistant_message = {
            "role": "assistant",
            "content": response.content,
            "timestamp": datetime.now().isoformat()
        }
        session.append_message(assistant_message)
        
        # 构造回复
        reply = platform_message.MessageChain([
            platform_message.Plain(text=response.content)
        ])
        
        return entities.StageProcessResult(
            result_type=entities.ResultType.CONTINUE,
            new_query=query,
            user_notice=reply
        )
    
    def _build_contextual_prompt(
        self, 
        current_message: str, 
        history: list[dict],
        user_name: str,
        preferences: dict
    ) -> str:
        """
        构造上下文提示词
        
        Args:
            current_message: 当前消息
            history: 消息历史
            user_name: 用户名
            preferences: 用户偏好
            
        Returns:
            构造的提示词
        """
        prompt = f"你是智能助手,正在与{user_name}对话。\n"
        
        if preferences:
            prompt += f"用户偏好: {json.dumps(preferences, ensure_ascii=False)}\n"
        
        prompt += "\n对话历史:\n"
        for msg in history[-5:]:  # 只包含最近5条消息
            role = "用户" if msg["role"] == "user" else "助手"
            prompt += f"{role}: {msg['content']}\n"
        
        prompt += f"\n用户最新消息: {current_message}\n"
        prompt += "请根据上下文回复用户:"
        
        return prompt
5.3 会话保存阶段
@stage.stage_class("session-save")
class SessionSaveStage(stage.PipelineStage):
    """会话保存阶段"""
    
    async def process(
        self,
        query: pipeline_query.Query,
        stage_inst_name: str,
    ) -> entities.StageProcessResult:
        """处理消息"""
        session = query.session
        
        # 将查询变量更新回会话变量
        session.variables.update(query.variables)
        
        # 保存会话到持久化存储
        try:
            await session.save(self.ap.persistence_mgr)
            console_notice = "会话数据保存成功"
        except Exception as e:
            console_notice = f"会话数据保存失败: {e}"
            self.ap.logger.error(console_notice)
        
        return entities.StageProcessResult(
            result_type=entities.ResultType.CONTINUE,
            new_query=query,
            console_notice=console_notice
        )

6. 会话状态管理

LangBot支持会话状态管理,可以实现复杂的交互流程:

class SessionStateManager:
    """会话状态管理器"""
    
    def __init__(self):
        self.states: dict = {}
    
    def set_state(self, session_key: str, state: str, data: dict = None):
        """
        设置会话状态
        
        Args:
            session_key: 会话键
            state: 状态名
            data: 状态数据
        """
        self.states[session_key] = {
            "state": state,
            "data": data or {},
            "updated_at": datetime.now()
        }
    
    def get_state(self, session_key: str) -> dict:
        """
        获取会话状态
        
        Args:
            session_key: 会话键
            
        Returns:
            状态信息
        """
        return self.states.get(session_key, {"state": "default", "data": {}})
    
    def clear_state(self, session_key: str):
        """
        清除会话状态
        
        Args:
            session_key: 会话键
        """
        if session_key in self.states:
            del self.states[session_key]

# 状态处理示例
@stage.stage_class("state-handler")
class StateHandlerStage(stage.PipelineStage):
    """状态处理阶段"""
    
    async def process(
        self,
        query: pipeline_query.Query,
        stage_inst_name: str,
    ) -> entities.StageProcessResult:
        """处理消息"""
        session = query.session
        current_state = session.state.get("current_state", "default")
        
        # 根据当前状态处理消息
        if current_state == "waiting_for_name":
            return await self._handle_name_input(query, session)
        elif current_state == "waiting_for_preference":
            return await self._handle_preference_input(query, session)
        else:
            return await self._handle_default(query, session)
    
    async def _handle_name_input(
        self, 
        query: pipeline_query.Query, 
        session: Session
    ) -> entities.StageProcessResult:
        """处理姓名输入"""
        user_name = query.message_chain.get_text()
        session.set_variable("user_name", user_name)
        session.state["current_state"] = "default"
        
        reply = platform_message.MessageChain([
            platform_message.Plain(text=f"你好,{user_name}!我会记住你的名字。")
        ])
        
        return entities.StageProcessResult(
            result_type=entities.ResultType.CONTINUE,
            new_query=query,
            user_notice=reply
        )
    
    async def _handle_preference_input(
        self, 
        query: pipeline_query.Query, 
        session: Session
    ) -> entities.StageProcessResult:
        """处理偏好输入"""
        preference = query.message_chain.get_text()
        preferences = session.get_variable("preferences", [])
        preferences.append(preference)
        session.set_variable("preferences", preferences)
        session.state["current_state"] = "default"
        
        reply = platform_message.MessageChain([
            platform_message.Plain(text=f"已记录你的偏好: {preference}")
        ])
        
        return entities.StageProcessResult(
            result_type=entities.ResultType.CONTINUE,
            new_query=query,
            user_notice=reply
        )

7. 会话清理和过期处理

LangBot支持会话清理和过期处理机制:

class SessionCleanupManager:
    """会话清理管理器"""
    
    def __init__(self, ap: app.Application):
        self.ap = ap
        self.cleanup_interval = 3600  # 1小时清理一次
        self.session_timeout = 86400  # 24小时无活动则过期
    
    async def start_cleanup_task(self):
        """启动清理任务"""
        while True:
            await asyncio.sleep(self.cleanup_interval)
            await self._cleanup_expired_sessions()
    
    async def _cleanup_expired_sessions(self):
        """清理过期会话"""
        now = datetime.now()
        expired_sessions = []
        
        for session_key, session in self.ap.sess_mgr.sessions.items():
            # 检查会话是否过期
            if (now - session.last_activity).total_seconds() > self.session_timeout:
                expired_sessions.append(session_key)
        
        # 清理过期会话
        for session_key in expired_sessions:
            session = self.ap.sess_mgr.sessions.pop(session_key, None)
            if session:
                # 保存会话数据到持久化存储
                await session.save(self.ap.persistence_mgr)
        
        self.ap.logger.info(f"清理了 {len(expired_sessions)} 个过期会话")

8. 性能优化

8.1 会话缓存
class SessionCache:
    """会话缓存"""
    
    def __init__(self, max_size: int = 1000):
        self.cache = {}
        self.max_size = max_size
        self.access_order = []
    
    def get(self, session_key: str) -> Session | None:
        """获取缓存的会话"""
        if session_key in self.cache:
            # 更新访问顺序
            self.access_order.remove(session_key)
            self.access_order.append(session_key)
            return self.cache[session_key]
        return None
    
    def put(self, session_key: str, session: Session):
        """缓存会话"""
        if len(self.cache) >= self.max_size:
            # 移除最久未访问的项
            oldest = self.access_order.pop(0)
            del self.cache[oldest]
        
        self.cache[session_key] = session
        self.access_order.append(session_key)
8.2 异步会话保存
class AsyncSessionSaver:
    """异步会话保存器"""
    
    def __init__(self, ap: app.Application):
        self.ap = ap
        self.save_queue = asyncio.Queue()
        self.save_task = None
    
    async def start_save_task(self):
        """启动保存任务"""
        self.save_task = asyncio.create_task(self._save_worker())
    
    async def _save_worker(self):
        """保存工作协程"""
        while True:
            try:
                session = await self.save_queue.get()
                await session.save(self.ap.persistence_mgr)
                self.save_queue.task_done()
            except Exception as e:
                self.ap.logger.error(f"保存会话时出错: {e}")
    
    async def queue_save(self, session: Session):
        """将会话加入保存队列"""
        await self.save_queue.put(session)

总结

LangBot的会话管理和上下文保持机制为构建智能聊天机器人提供了强大的支持。通过会话管理器、会话对象、上下文变量管理等组件,LangBot能够有效地管理多轮对话中的状态和信息。

关键要点包括:

  1. 会话识别:支持按用户、群组等维度识别会话
  2. 上下文保持:通过消息历史和上下文变量保持对话连贯性
  3. 状态管理:支持复杂的会话状态流转
  4. 持久化存储:将会话数据持久化,支持服务重启后恢复
  5. 性能优化:通过缓存、异步保存等技术优化性能

在实际应用中,开发者可以根据具体需求定制会话管理策略,例如调整会话超时时间、优化上下文提示词构造、实现特定的会话状态机等,以构建更加智能和用户友好的聊天机器人应用。

参考资料

  1. LangBot官方文档 - 会话管理
  2. Session模块源码
  3. 会话管理器实现
  4. 持久化存储
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CarlowZJ

我的文章对你有用的话,可以支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值