上下文断在端云切换这一步,通常只有两种可能:切换请求没有把会话标识续上,服务端把这次调用当成全新对话;或者端侧能容纳的上下文比云侧小,超出的历史被截掉。排查顺序不能反——先确认会话有没有续上,因为会话断链和输入被截断在下游表现上很像,但补字段的代价远小于改历史策略;确认会话续上之后,再比对端侧和云侧实际读到的范围,才有依据判断是不是窗口问题。下面五个步骤按这个顺序展开,每一步都能靠日志、请求体或命令验证,不依赖猜测。
判断方向:同一会话在云侧正常、切到端侧答非所问,先排除会话标识丢失,再用同一段输入分别打端侧和云侧,看返回引用的是历史前半段还是只引用后半段。适用场景是端云双通道接入同一产品;操作上按“定位截断点—核对会话字段—做对照请求”三步走;验证方式是同一份输入两次返回所引用的事实是否一致。风险边界:端侧可能另有一套历史管理策略,字段补齐和窗口对齐都要结合具体接入方式确认,不要只改一处就宣布修好。
在会话记录里标出最后一轮被完整读到的位置
目的是确认上下文从哪一轮开始失效。记录来源一般有三个:客户端调试面板里的原始请求、网关或反向代理的访问日志、应用侧在发请求前打印的请求体。三者取其一即可,但要求能拿到完整 body,只看响应对不上账。
拿到同一会话相邻两轮的请求体后,比较 messages 数组(或你接入的等价字段)的长度和最后一条 user 内容。如果第二轮请求里的历史条数比第一轮少,或者第一轮里存在过的某条早期消息在第二轮消失,说明在客户端组装阶段就被裁掉了;如果两轮条数都在增长、内容完整,问题出在服务端读取,去看返回里有没有截断相关标记或错误码。
# 保存两轮请求体后,比较结构和历史条数
jq '.messages | length' round1.json
jq '.messages | length' round2.json
# 逐条对比首条与末条,看早期消息是否还在
jq -r '.messages[0].content' round2.json | head -c 200
判定截断的观察点建议固定为三个:历史条数是否单调不减、最早一条 system 或开场白是否仍在、最后一条 user 内容是否与用户实际输入逐字一致。三个点任一不满足,就先按截断方向查;三个点都正常,再回到会话标识这条线上。
检查切换请求里会话标识是否被带上
会话断链是最常见的原因:切换开关改了,但请求构造那一段仍然在生成新的会话标识,服务端每次看到的都是新会话,自然没有历史可续。检查方式是翻到切换前后的两条请求体,只看会话相关字段。
{
"model": "<端侧或云侧模型标识>",
"session_id": "<同一会话保持不变的占位值>",
"messages": [
{"role": "system", "content": "..."},
{"role": "user", "content": "..."}
]
}
上面字段名仅作示意,真实字段名(可能是 session_id、conversation_id、chat_id 或放在 header 里)以官方文档为准,接入前先核对一遍。核对后做两件事:一是确认该字段在切换开关前后取值相同;二是确认切换后没有被新生成的 UUID 覆盖。若接入方式是服务端托管历史,还要确认切换后仍指向同一个会话资源,而不是重新创建。
把同一段输入分别压到端侧和云侧,对比返回的引用范围
确认会话续上之后,才轮到判断容量差异。构造一段长输入,前段和后段各放一个只出现一次的标记事实,比如前段写“项目代号是 A-7391”,后段写“本次联系人代号是 B-2058”,最后提问针对前段标记。分别打端侧和云侧,只观察返回引用了哪一段。
“只看到后半段”的典型特征有:能准确复述后段标记,对前段标记答错或给出与输入不符的编造内容;模型在回答里声明只收到部分内容;或者两次返回对同一问题的答案方向不同,而后段相关的问题两边都对。出现这些特征,端侧可容纳的上下文比云侧小的可能性就比较大,但仍要排除端侧是否另做了摘要压缩——压缩也会表现为“只记得后半段”。
写一个最小可复现请求,固定输入只改切换开关
把偶发问题变成稳定复现,关键是除了切换开关之外所有变量都不动:同一份 messages、同一个 temperature、同一个会话标识、同一时间窗内连续发两次。
POST <接入地址>
{
"model": "<固定值>",
"session_id": "<固定占位值>",
"messages": [<与上一次完全一致的数组>],
"switch": "<cloud | edge>"
}
对照组设置方式:先发云侧拿到基线返回,再发端侧,然后立刻再发一次云侧。第三次云侧返回若不等于第一次,说明输入本身或会话状态在变化,前面对比不成立,需要重置会话重做。记录方式建议每条保存请求体、返回全文、返回里的 request id(或等价追踪标识)和时间戳,按顺序编号,便于事后追。
确定处理策略:切回时重传摘要还是缩短历史
两种修法各有适用条件。重传摘要适合历史长、但真正有用的信息集中在前几轮的场景:客户端维护一份随轮次更新的摘要,切到端侧时把摘要放在首条 system 位置,再接最近若干轮原文,保证端侧看到的是压缩后的完整脉络。实现要点是摘要必须包含后续追问依赖的实体(人名、代号、结论),并且保留“信息来源是哪一轮”的痕迹,方便用户追问时回溯。风险是摘要本身会丢细节,对需要逐字引用的任务(合同条款、代码片段)不合适。
缩短历史适合会话主题切换频繁、早期轮次确实无用的场景:设定一个按条数或 token 的保留上限,切到端侧前主动裁到上限内,并在界面上提示“已精简早期对话”。实现要点是裁剪按完整轮次切,不要从中间截断一条消息;同时把裁剪前的完整历史留在本地,允许用户回看。风险是用户回头引用早期内容时模型无法响应,需要在提示文案里讲清楚。
验证是否恢复正常,按三步做:用第 4 节的最小请求重跑一次,确认端侧返回能引用前段标记;切回云侧再问同一个问题,两次答案方向一致;连续追问两到三轮,确认历史仍在增长而不是每轮被重置。三步都通过再收工,只过第一步通常只是运气。