Session Stitching:为什么你的会话 ID 是个谎言
一名用户在上午 9 点开始在她的电脑上与你的智能体谈判合同。她收到一条 Slack 消息,在午休时间切换到手机问了一个澄清问题,并在下午 4 点重新打开电脑标签页来修改草案。对她来说,这是一项任务 —— 处理一份合同的三个小时工作。对你的系统来说,这是两个设备上的三个会话,每个都有自己的 conversation-id,每个都有自己的记忆窗口,每个都呈现全新的问候并要求她重新粘贴已经讨论过两次的草案。
Bug 不在模型中。Bug 在于你的平台将“会话 (session)” —— 一个关于单一连接的传输层产物 —— 编码为上下文单位,而你的用户将“任务 (task)” —— 即合同 —— 编码为上下文单位。市面上的每个框架都悄悄地混淆了这两者,而它们之间的差距正是智能体 UX 损耗了一半的地方。
这并不是一个冷门的抱怨。一旦你开始记录任务级别的追踪(trace),你会发现很大一部分“新对话”实际上是未完成任务的延续 —— 用户放弃了手动缝合它们,转而重新开始。你称之为“参与度 (engagement)”的产品 KPI,实际上部分衡量了用户在为你的抽象缺失支付代价。
框架丢给你一个 Session-ID 并称之为记忆
打开过去两年中的任何智能体 SDK,持久化的故事都大同小异。LangGraph 要求你传递一个 thread_id;检查点(checkpointer)按线程保存图状态,与其他每个线程分开,并在你使用相同的 ID 重新调用时恢复。OpenAI Agents SDK 提供一个由会话 ID 索引的 Session (SQLiteSession("conversation_123"));同一个会话,完整的历史;新会话,一片空白。Claude Agent SDK 将会话持久化到磁盘,以便你稍后返回。Google 的 ADK 提供了一个 Resume 功能,可以接续中断的工作流运行。
这些原语在它们针对的层级上是正确的 —— 它们可靠地为单个逻辑运行持久化状态。但它们都没有定义从用户的角度来看什么是“逻辑运行”,而教程中提供的默认方案是:每个 WebSocket 连接一个会话,每个浏览器标签页一个线程,每个进程一次恢复。平台决定了边界在哪里,而边界几乎总是落在错误的地方。
当你追踪一个真实用户时,缝隙并不在框架划定的地方。用户不会因为他们的 TLS 连接超时而切换任务。他们只有在完成合同谈判并开始引导供应商入驻时才会切换任务。传输层的 session-id 和用户的心理任务边界是两件不同的事情,只是有时恰好重合,而你的产品正将连续性押在巧合之上。
Task-ID 必须是一等公民 —— 且是正交的
解决方法不是让会话存活得更久。长会话会积累无关的历史记录,如果你以后升级模型来修剪上下文,你无法决定保留什么,因为你从未记录过哪些内容属于什么。
解决方法是引入一个对用户有意义且与 session_id 正交的 task_id。会话仍然存在 —— 它们描述了一个连接、一个设备、一个进程的生命周期 —— 但它们不再是记忆的单位。任务才是记忆的单位,而会话只是恰好属于某个任务的一段活动。
具体来说:
- 任务有一个用户理解的名称(例如“Acme 合同修订”,而不是 “conv-7e3f”)。用户显式命名它,或者你的智能体从第一轮开始建议一个名称并让用户接受或重命名。
- 任务有一个工作集 (working set):构成其进行中状态的文档、决策、开放性问题和部分输出。这个工作集在不同会话之间是持久的,也是你构建恢复摘要的基础。
- 会话有一个
task_id外键。当用户在不同设备上重新连接时,平台会查找该用户的未完成任务,将其展示出来,并询问当前的会话接续哪一个任务 —— 或者允许用户显式开始一个新任务。 - 智能体的续接 UX 是“你之前正在处理 Acme 合同修订 —— 从上次中断的地方继续吗?”并附带一段状态摘要。它不是“昨天的聊天”这种按时间顺序排列的滚动条,强迫用户重新推导他们当时在做什么。
ChatGPT Projects 是目前最接近这种正确形态的主流模式:项目是一个持久的容器,对话存在于其中,用户可以切换设备而不会丢失进度。它并不完美,但 它证明了在会话之上建立任务抽象在消费级规模上是可行的。
重点并不是每个人都需要一个 Projects 克隆。重点在于你的数据模型需要在会话和任务之间建立一个显式的关联 (join),由你的团队拥有,暴露给你的评估套件,并在你的遥测数据中可见。如果你没有这种关联,你就不具备任务的连续性 —— 你有的只是侥幸。
跨设备连续性本质上是伪装的授权问题
一旦你致力于持久的、跨设备的任务,你为临时会话构建的鉴权模型就不再适用了。
当用户、设备和对话共存亡时,短期的会话绑定凭据是没问题的。契约是:此令牌授权此连接;连接丢失,令牌失效。但一个在不同设备间存续的任务需要不同的契约:此令牌授权从不同设备恢复此任务,可能没有原始 Cookie,可能是在重启后,也可能是在用户同时从另一个设备活跃连接时。
最近关于跨设备流的工作已将其推向了具体的协议领域。IETF 的 Cross-Device Flows BCP 草案正式确定了会话转移流,用户在“授权设备”上授权转移,然后在“消费设备”上使用会话,且跨边界保留状态。设备绑定会话凭据 (DBSC) 将会话 Cookie 绑定到设备持有的私钥,因此无法被静默窃取。这两点都很重要,因为它们告诉你,你的跨设备恢复将面临与鉴权协议领域一直在努力解决的相同约束 —— 只是现在你是在一个充满进行中智能体状态(包括草案合同和部分执行的工具调用)的持久任务库之上进行协商。
