会话管理
大模型应用与传统的请求-响应模型不同,用户期望的是连贯的多轮对话,而非孤立的问答。会话管理负责在无状态的 LLM 上构建有状态的对话体验,涉及上下文维护、记忆压缩、状态持久化等问题。好的会话管理让用户感受到被理解和记住,差的则会重复信息、逻辑断裂。
无状态性挑战
LLM 的原生调用是无状态的,每次请求独立,模型不知道之前说过什么。这种设计简化了服务端实现,但不符合人类的对话习惯。用户说"帮我订机票",接着说"要上午的",第二次请求必须知道第一次的目的地才能理解"上午的"是什么的上午。解决方法是在每次请求时拼接历史对话,让 LLM 拥有完整的上下文。
最简单的实现是在客户端维护消息数组:
messages = [
{"role": "user", "content": "帮我订明天去北京的机票"},
{"role": "assistant", "content": "好的,请问您偏好什么时间?"},
{"role": "user", "content": "要上午的"}
]
response = llm.invoke(messages)
messages.append({"role": "assistant", "content": response.content})这种方式的问题是消息长度会线性增长,最终超出模型的上下文窗口,或者推理成本过高。需要一种机制来管理上下文长度,在保持对话连贯性的同时控制 token 消耗。
上下文窗口
上下文窗口是模型一次能处理的最大 token 数,包括输入和输出。GPT-3.5 是 4K,GPT-4 是 8K/32K,Claude 2 是 100K,开源模型如 LLaMA 2 是 4K。窗口大小直接影响能处理多长的对话、多大的文档。超长上下文是近年来的发展方向,Claude 3 支持到 200K,Gemini Pro 理论上支持到 1M,但实际使用中长上下文的效果会衰减,模型对开头和中间的内容关注度下降(Lost in the Middle 现象)。
上下文管理策略需要根据应用场景选择。客服机器人通常只需要最近几轮对话,因为用户问题的上下文是短期的。代码分析助手可能需要整个文件的上下文,因为变量定义和使用的距离可能很远。文档问答需要检索相关片段,而不是把整个文档塞进上下文。
Token 消耗直接影响成本和延迟。每轮对话都重复发送历史消息,token 量按 O(n²) 增长(n 是轮次)。10 轮对话,平均每轮 100 tokens,累积到第 10 轮时单次请求就需要 1000 tokens。优化方向包括:压缩历史、滑动窗口、语义保留。
记忆机制
滑动窗口是最简单的记忆策略,只保留最近 N 轮对话。N 的选择需要权衡连贯性和成本,N=3 可能丢失关键信息,N=10 可能浪费 tokens。改进方法是指针网络,用小模型评估每条历史消息的重要性,只保留与当前问题相关的部分。这需要一个二分类模型,输入是当前问题和历史消息,输出是相关性分数。
摘要压缩用 LLM 总结旧对话,将详细内容转化为精简摘要。"用户询问了北京到上海的机票,偏好上午出发,预算 1000 元以内"比原始对话更节省 tokens,同时保留关键信息。摘要的时机可以是每隔固定轮次(每 5 轮总结一次),或者基于长度阈值(tokens 超过 2000 时触发)。摘要的质量取决于 LLM 的理解能力,小模型可能遗漏重要细节。
长期记忆将对话历史中的关键信息(用户偏好、任务结果、知识点)提取并存储,在后续对话中检索使用。用户的说法"我每次都要靠窗座位"应该被提取为偏好规则,下次订票时自动应用。长期记忆通常用向量数据库实现,将对话片段向量化,通过相似度检索相关记忆。关键在于设计什么值得记忆:实体(人名、地名、事件名)、关系(用户的社交网络)、模式(用户的习惯和偏好)。
分层记忆结合短期、中期、长期。短期记忆是最近几轮对话的完整内容,用于维持连贯性。中期记忆是对话摘要,用于回顾对话主题。长期记忆是跨会话的知识,用于个性化体验。每次请求时从三层记忆中检索相关信息,动态组装上下文。这种设计借鉴了人类的记忆机制,工作记忆、短期记忆、长期记忆各有用途。
对话状态
对话状态是对话过程中累积的结构化信息,包括用户意图、槽位填充、任务进度。订票对话的状态包括出发地、目的地、日期、时间、座位偏好等字段,每轮对话更新这些字段,当所有必填字段都有值时执行下单。状态机是常见的管理方式,定义状态转换规则,从"开始"到"收集信息"到"确认"到"完成"。
槽位填充是从对话中提取结构化信息的任务。"我要明天去北京"需要提取日期(明天)、目的地(北京)。LLM 可以通过提示词完成这个任务,"从以下对话中提取出发地和目的地,以 JSON 格式返回"。槽位验证确保提取的信息有效,日期不能是过去的时间,目的地必须是机场代码或城市名。缺失槽位需要追问,"请问您从哪里出发?"
多任务并行是复杂场景。用户可能在同一个会话中同时进行多个任务,"帮我查一下明天的天气,顺便订个去上海的票,另外提醒我下午三点开会"。每个任务有独立的状态,需要任务管理器来切换和协调。一种方法是给每个任务分配独立的 Agent,由主 Agent 负责任务分发和结果汇总。
持久化与恢复
会话持久化将对话状态保存到存储系统,支持跨设备、跨时间的对话延续。用户在手机上开始对话,回家后在电脑上继续,需要从数据库恢复会话状态。Redis 是常见选择,它的内存数据库提供低延迟访问,适合实时会话。长期存储可以用 PostgreSQL 或 MongoDB,保存会话历史、用户偏好、任务结果。
会话过期和清理是运维问题。不是所有会话都需要永久保存,一般对话 7 天后价值降低,可以归档或删除。定期清理任务需要识别非活跃会话(最后更新时间超过阈值),将历史消息转存到冷存储,只保留摘要在热存储。隐私法规(如 GDPR)要求用户有权删除个人数据,需要设计数据清除机制。
分布式环境下的会话管理面临一致性挑战。多个服务实例处理同一用户的请求,需要共享会话状态。Redis 的分布式锁可以保证同一时间只有一个实例修改会话,避免竞争条件。更复杂的场景是会话迁移,用户从服务器 A 连接到服务器 B,需要无缝迁移会话状态,WebSocket 的断线重连机制可以处理这种场景。
用户体验
会话管理的最终目标是自然的对话体验。用户不应该感知到上下文窗口的限制,不应该重复说过的信息,不应该因为长时间没对话而丢失记忆。这需要精心设计记忆策略和状态管理,让 AI 伴侣般的感受成为可能。
主动记忆是未来的方向。用户说"我儿子今年上小学",AI 应该主动记住这个信息,而不只是被动存储。后续对话中,当话题涉及教育、儿童时,AI 能自然提及"您儿子上小学的情况如何"。这种主动性需要 LLM 具备更强的信息提取和关联能力,也是 Agent 记忆机制的研究前沿。
跨会话的个性化是长期目标。用户在不同时间的对话应该共享偏好和知识,形成持续的人格化体验。这需要长期记忆的积累和检索,也需要隐私保护的设计。用户应该能控制哪些信息被记住、哪些被遗忘,透明的记忆管理让用户对 AI 伴侣建立信任。