MyContext 上下文丢失先查会话隔离键,再对截断长度和拼接顺序

文章导读
接入 MyContext 后第二轮对话上下文不连续,通常不是模型的问题,而是接入侧有两处没对齐:一是会话隔离键(session_id / tenant_id / agent_id)在两次请求里是否稳定传入,二是截断长度(max_tokens / keep_recent)和消息拼接顺序有没有把前面轮次裁掉或排错。建议按这个顺序排查:先在会话记录里找到从哪一轮开始缺失,再核对隔离键与存储键,然后检查截
📋 目录
  1. A 在会话记录里定位上下文从哪一轮开始不连续
  2. B 核对会话隔离键与存储键是否稳定传入
  3. C 检查截断长度和消息拼接顺序
  4. D 用最小多轮请求做回归验证
A A

接入 MyContext 后第二轮对话上下文不连续,通常不是模型的问题,而是接入侧有两处没对齐:一是会话隔离键(session_id / tenant_id / agent_id)在两次请求里是否稳定传入,二是截断长度(max_tokens / keep_recent)和消息拼接顺序有没有把前面轮次裁掉或排错。建议按这个顺序排查:先在会话记录里找到从哪一轮开始缺失,再核对隔离键与存储键,然后检查截断与拼接,最后用两轮最小请求做回归。隔离键不稳,表现是串话或空会话;截断配置不当,表现是明明有历史却读不到——两种现象很像,先区分再调参能少走弯路。

先判断是「取不到」还是「取到了被裁掉」:若同一 session_id 的两轮请求键值不一致,问题在隔离键与存储键;若键值一致但最终 prompt 缺少历史消息,问题在截断长度或拼接顺序。按「找到丢失起点 → 核对键 → 检查截断拼接 → 两轮回归」的顺序处理,每一步都用日志或打印验证,不要凭感觉直接放大窗口。

在会话记录里定位上下文从哪一轮开始不连续

不要一上来就把窗口调大,先把三段数据对齐:Agent 侧发出的输入、MyContext 返回的历史、以及最终送进模型的 prompt。按轮次编号(turn 1、turn 2…)和消息 role(system / user / assistant / tool)逐条打印,标出第一处缺失的消息或字段——是整条历史都没回来,还是回来了但没进 prompt,还是进了却被排在 system 之前。

常见起点有三种:第二轮开始就完全没有历史,多半是会话键变化换到了另一个会话;第三轮之后才丢,通常是截断裁掉了较早轮次;工具调用轮次丢结果,多是拼接时漏了 tool 结果消息。定位到具体轮次和 role 后,再决定去调键还是调截断。

# 伪日志格式,按轮次逐行打印便于比对
turn=1 session_id=... role=user      content_len=...
turn=1 session_id=... role=assistant content_len=...
turn=2 session_id=... role=user      content_len=...
turn=2 inject_history=0 或 history_turns=[...]   # 缺失就暴露在这一行

核对会话隔离键与存储键是否稳定传入

MyContext 这类上下文服务通常靠一个组合键做隔离,键变了就等于换了会话,历史自然读不到。建议键值由 tenant_id、agent_id、session_id 三段组成,缺一段都可能落到共享或空会话上。下面是可以替换的占位骨架:

MyContext 上下文丢失先查会话隔离键,再对截断长度和拼接顺序
# 组合存储键,字段名按实际 SDK 替换
storage_key = f"{tenant_id}:{agent_id}:{session_id}"

# 接入侧建议显式传入,不要依赖默认值
mycontext.write(key=storage_key, messages=msgs)
mycontext.read(key=storage_key, limit=keep_recent)

验证方式:用同一个请求把三段键值和最终 storage_key 打印出来,分别在写入和读取两处打印,确认第二轮与第一轮完全一致。要重点看三类问题:session_id 每轮新生成(表现为永远空会话)、tenant_id 或 agent_id 为空(表现为串话)、同一个 session_id 被不同用户复用(表现为覆盖写入)。这些都能通过键值打印直接观察。

检查截断长度和消息拼接顺序

键稳定后仍丢历史,就要看截断和拼接。截断一般由 max_tokens 和 keep_recent 这类参数控制,风险在于:按 token 裁剪时先丢 system 和工具结果,或者 keep_recent 只保留最近一轮,导致第二轮拿不到第一轮的关键内容。拼接顺序则常见把新消息放在历史之前、或把 system 放到历史之后。参考骨架:

MyContext 上下文丢失先查会话隔离键,再对截断长度和拼接顺序
# 占位配置,参数名以实际实现为准
context = {
    "max_tokens": 4096,
    "keep_recent": 6,          # 保留最近 N 轮/条
    "reserve_system": True,    # system 不参与裁剪
    "keep_tool_results": True, # 工具结果优先保留
    "order": ["system", "history", "user"],  # 拼接顺序
}

验证方式:打印最终消息列表和总长度,逐条与预期保留项对比——system 是否还在、第一轮关键实体是否还在、工具结果是否还在、顺序是否符合 order。如果顺序对但内容被裁,就是 max_tokens 或 keep_recent 偏小;如果顺序错但内容还在,就是拼接逻辑的问题。

用最小多轮请求做回归验证

改完键或截断后,用两轮最小请求确认上下文连续且没有串到别的会话。核心是断言第二轮能读到第一轮的关键实体,并核对日志里的会话键一致。

# 伪代码,替换为实际调用
key = f"{tenant_id}:{agent_id}:{session_id}"
entity = "订单号A123"   # 第一轮埋一个独特实体

r1 = agent.chat("记住这个%s" % entity, key=key)
r2 = agent.chat("刚才的订单号是什么?", key=key)

assert key in r1.log and key in r2.log      # 会话键一致
assert entity in r2.final_prompt            # 上下文命中
assert entity in r2.reply                   # 端到端可用
# 再换一个 session_id 发同样两轮,确认读不到 entity(未串话)

如果 entity 出现在 final_prompt 但没出现在回复里,问题在模型侧而非上下文;如果 final_prompt 里就没有 entity,回到前两步重新核对键与截断。换 session_id 的对照请求能验证隔离是否生效,这一步建议保留到修复确认之后。整套检查只依赖配置、日志和请求本身,换环境时按实际字段名替换即可。