一段对话在两轮之间存在哪里
既然每一轮都是一次自包含的请求,对话其余的部分在哪?每一轮存下什么、什么永远不存,逐字窗口是什么,更早的轮次如何被恰好折进滚动摘要一次,单轮仍然装不下时会发生什么,以及哪些事实活得比会话久。
每一轮都是一次自包含的请求:模型在两轮之间不保留任何东西,没进这份请求的,对它而言就不存在。于是有个明摆着的问题——如果模型什么都不记得,那对话在哪?
在我们这边,分三层,三种不同的寿命。每一轮都是从这三层里装配出来的,而层与层的区别不在重要性,在于它们往回够多远。
三层
存下什么,以及什么永远不存
每一轮恰好写两行:访客说了什么,智能体答了什么。两行都用该用户自己的密钥字段级加密存放,和他其余内容的处理方式一致(见隐私即设计)。
不写的那部分同样是刻意的。模型发起的工具调用、这些工具返回的原始载荷、它一路做的中间推理——一概不落库。它们只为一轮、在一份请求里存在过,然后就没了。留下的对话记录就是真正说出口的话——这也是为什么一段存下来的对话能被人直接读,不必在机器的草稿纸里跋涉。
逐字窗口
最近的若干轮逐字进入请求——目前是最后十二条消息。正是这一层让智能体感觉像在跟你对话:指代能解析,你的更正它记得住,「第二个」是有所指的。
窗口用条数而不是 token 预算来划,是有意的。条数是可预期的——你能推断出智能体眼前还剩什么。当这个窗口本身也装不下时,请求级的优先级接管(见装不下时先丢什么),这些消息里最旧的先走。
滚动摘要
比窗口更早的内容不会被丢掉。一旦累积的消息超过窗口容量,多出来的那些——最旧的——就被折进这个会话的摘要,同时一个游标向前推过它们。
游标是这里值得知道的部分。摘要不是每次都从整段对话重新生成,而是增量续写:已有的摘要,加上刚刚滑出窗口的那几条,生成新的摘要,而那几条再也不会被摘要第二遍。正是它让长对话的维护成本不随长度上涨,也解释了摘要为什么是逐渐沉淀而不是被反复改写——早期的上下文保持着它第一次被记下的样子。
摘要有上限(几百个 token),存在会话上,和其余内容一样加密。在请求里它是历史上方的一块。
单轮仍然装不下的时候
上面两个机制是稳态下的。单独一轮仍然可能太大——一个很长的问题、粘进来一份大文档、知识库这一轮贡献得格外多。
这时请求级预算会先丢最旧的历史,而被它丢掉的那些轮次会被压进这一轮的摘要块,而不是简单移除。这个区别要紧:丢弃是悄悄失去内容,压缩只失去措辞。没有任何东西为了让请求装下而被悄悄删掉。
什么活得比会话久
上面这三层都属于同一段对话。有两样东西不属于:
- 关于这个客户的长期事实——随着对话发生被提炼出来,去重之后同一条事实是被更新而不是不断堆积,取用时按相关度而不是新旧。它们跟着这个人跨会话、跨渠道。怎么提炼、召回什么,见逐客户记忆。
- 一份简短画像——定期从近期消息刷新,携带的是长期背景而不是具体事实。
所以新开一个会话确实是一段全新的对话——没有历史、没有摘要——但它不是失忆的。智能体关于这个人学到的东西还在。
这对你意味着什么
- 会话就是「保持同一个 session id」的那一段。 网页挂件为每位访客保留一个;API 调用方自己决定。开一个新的,就是在不丢掉客户信息的前提下,主动放下当前这条线索。
- 长对话是逐渐退化,不是突然失效。 最旧的轮次先失去措辞,再失去内容要点,最后才彻底消失。
- 导出和记录看到的是对话,不是机器的运转过程。 因为工具往返从来没有落库,你读回来的就是一个人会读到的东西。