接入 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 三段组成,缺一段都可能落到共享或空会话上。下面是可以替换的占位骨架:
# 组合存储键,字段名按实际 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 放到历史之后。参考骨架:
# 占位配置,参数名以实际实现为准
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 的对照请求能验证隔离是否生效,这一步建议保留到修复确认之后。整套检查只依赖配置、日志和请求本身,换环境时按实际字段名替换即可。