网关中的长对话
本页写给配置上下文配置和上游的管理员,也写给长对话被截断或被服务商以过长为由拒绝的用户。读完后你会知道网关何时、如何为长对话生成摘要,压缩后模型收到什么,以及要付出什么代价。最先要检查的是模型的上下文窗口:没有设置时,网关按 1,000,000 token 计算。
压缩做什么
压缩让对话始终不超出模型的上下文窗口(单次请求最多能容纳的 token 数),默认开启。对话接近窗口上限时,由同一个模型为目前为止的对话写一份摘要,此后截断点之前的全部内容都由这份摘要替代。客户端里显示的历史不受影响。开启了保存对话、模型也能用 OpenViking 的 grep 和 read 工具时,摘要末尾会附上查找被压缩内容的方法。
在 Studio 中的位置:上下文配置 → 长对话。
| 设置 | 默认值 | 作用 |
|---|---|---|
| 压缩 | 开启 | 对话接近上下文窗口时,用摘要替换较早的部分。 |
| 压缩时机 | 窗口的 0.9 | 窗口用到多大比例时开始压缩。 |
| 摘要长度上限 | 8,000 token | 模型写一份摘要最多能用多少 token。 |
| 默认上下文窗口 | 未设置(按 1,000,000 token 计算) | 上游没有列出的模型用这个窗口,见设置模型的上下文窗口。 |
| Agent 自管上下文窗口 | 关闭 | 实验性功能,由模型自己开启新窗口,见下文。 |
对话会一直沿用开始时的上下文配置,所以修改配置只影响新对话,见修改何时生效。取值范围和 API 字段名见配置参考。
何时压缩
每收到一条新的用户消息或一个工具步骤(客户端回传自己工具执行结果的请求,即请求类型中的工具步骤),网关都估算一次窗口用量:取服务商为这段对话上一次回复报告的 token 用量,加上之后新增消息的估算值。估算值达到压缩时机(默认是窗口的 0.9)时,网关先压缩,再转发请求。
例外情况:
- 服务商没有报告用量。 拿不到用量时(例如流式 Chat Completions 没有设置
stream_options.include_usage),网关估算整个请求。 - 不触发压缩的请求。 辅助请求、子 Agent 请求和 Token 计数请求不会触发压缩。
- OpenViking 工具往返不检查。 网关只在请求到达时检查窗口用量,所以它在一次请求内部代为执行的 OpenViking 工具往返不会被压缩。工具轮数和 Token 预算默认都不限;要让单次请求不超出窗口,请设置每次请求的工具轮数或 Token 预算(见上限)。
设置模型的上下文窗口
网关要知道每个模型的上下文窗口有多大,才能及时压缩。它按以下顺序取第一个设置了的值:
- 上游上下文窗口中该模型的条目,按发给上游的模型名(别名换算之后)匹配。
- 上下文配置的默认上下文窗口。
- 都没有设置时,按 1,000,000 token 计算。
模型的窗口小于 1,000,000 token 时,请设置前两项之一。否则网关压缩得太晚,还没等到压缩,服务商就会以对话过长为由拒绝请求。如果你没有权限修改上游或上下文配置,请联系负责管理网关的人,见谁能修改什么。
设置并确认模型的窗口:
- 在 Studio 中打开上游,编辑对应上游,在模型 → 上下文窗口中添加模型名及其窗口大小(单位为 token,至少 1,024)。上游设置保存后立即生效。如果想让这个上游没列出的所有模型都用同一个窗口,改为在上下文配置 → 长对话中设置默认上下文窗口,这只对新对话生效。
- 发一条消息,然后在请求日志标签页展开这条请求。上下文窗口一行应显示“约 … / N Token”,N 就是你填的窗口大小。
摘要怎么生成
网关向同一个上游、同一个模型额外发一次非流式请求。客户端的系统提示词、工具和思考设置保持不变,所以大部分内容能命中服务商的提示词缓存。纯文本摘要做不到的设置会被去掉:强制的工具选择改为不调用工具,结构化输出格式和停止序列也不保留。
这次请求包含截断点之前的对话,以及一条让模型为自己写摘要的指令。之前的摘要会并入新摘要。摘要要覆盖:
- 用户的目标和最新请求的原文;
- 关键决定和当前进度;
- 文件、路径和标识符;
- 出过的错误和修正;
- 未完成的事项;
- 最近几次工具结果里的要点。
摘要是纯文本,最长摘要长度上限(8,000)个 token。思考内容也计入输出上限,而有些模型(如 DeepSeek V4.1)即使请求里没有思考设置也会默认思考,所以网关总在摘要长度上限之外再留 16,000 token 的输出余量;客户端手动指定了思考预算时,改为加上这份预算。这次请求和其他请求一样由服务商计费。
压缩之后模型收到什么
截断点的位置取决于请求类型:
| 请求 | 截断点 | 模型收到的内容 |
|---|---|---|
| 新的用户消息 | 紧挨在这条消息之前 | system 和 developer 消息、作为一条用户消息的摘要,以及这条新消息 |
| 工具步骤 | 最近一次工具结果之后 | system 和 developer 消息,以及摘要;模型从摘要出发继续完成任务 |
摘要消息开头说明网关替换了对话较早的部分、这段内容不是用户写的;结尾附上这段对话开头的网关说明和用户画像,所以压缩后它们仍然保留。
之后凡是重发这段历史的请求,包括辅助请求和 Token 计数请求,都做同样的替换;子 Agent 通常发送自己单独的历史,不受影响。每次压缩让服务商缓存在截断点失效一次,之后照常命中。
截断点之前的原文一概不保留,最近几轮也一样。模型之前的回复和其中的思考内容都依赖被替换掉的历史,只保留一部分会破坏 Claude 的思考签名。截断点之后生成的内容照常发送,包括思考内容。
查找被压缩的内容
对话本身仍保存在 OpenViking 里。开启了保存对话、这段对话也提供了 openviking_grep 和 openviking_read 工具时,摘要末尾会附上查找方法:
- 这段对话对应的 OpenViking 会话(
viking://user/<user_id>/sessions/<session_id>/),以及会话中消息的存放位置; - 检索步骤:先用具体的关键词 grep 会话,拿到行号,再读取匹配位置前后的内容。
截断点落在一轮中间时,截断点之前的部分会立即保存到 OpenViking,模型在这一轮里就能检索到。
在请求日志中查看压缩
在请求日志标签页展开一条请求(见请求日志),上下文窗口一行会显示:
| 显示内容 | 含义 |
|---|---|
| 约 X / Y Token(P) | 估算的窗口用量,以及网关采用的窗口大小 Y |
| 发送时较早的历史已被替换 | 这次请求用摘要替代了截断点之前的历史 |
| 生成了约 N Token 的摘要,用时 T | 这次请求触发了压缩,写摘要用了 T |
| 压缩失败(原因),已发送完整历史 | 见压缩失败时 |
摘要已保留,但用量仍达到压缩时机(still_over_threshold) | 摘要已写好并用上,但对话仍占满窗口,见压缩失败时 |
压缩失败时
摘要请求失败,或者回复里调用了工具、内容为空、因长度上限被截断时,网关改为发送完整历史。请求详情会显示压缩失败及原因,并且 60 秒内不再为这段对话尝试压缩。
只有一种情况会保留摘要:摘要写完后用量仍达到压缩时机(例如系统提示词和工具本身就占了窗口的大部分)。这时网关照常使用这份摘要,同时记一次失败,同样等 60 秒再尝试。
各个原因的含义和处理方法见长对话超出上下文长度。
副作用
不再生成 Working Memory 摘要。 摘要由对话所用的模型生成,不使用 OpenViking 的会话摘要。网关新建的 OpenViking 会话都关闭了 Working Memory:提交时照常归档消息、提取记忆,只是不生成会话摘要。
记忆预算重新计算。 每压缩一次,单个上下文窗口预算(见召回记忆)就重新计算,截断点之前补充过的记忆可能再次补充。
客户端仍发送完整历史。 网关压缩后,客户端还是发送完整历史,网关在每个请求里替换截断点之前的部分。客户端看到的用量很小,所以按用量决定何时压缩的客户端很少再自己压缩。
特别长的会话可能超出请求大小上限。 客户端的历史越来越长,特别长的会话最终可能超出请求大小上限:请求体超过 32 MiB 会被拒绝,返回 HTTP 413(见客户端收到网关返回的错误)。运维人员可以用 max_body_bytes 调整这个上限(见网关字段);网关前面的反向代理允许的请求体也不能比它小(见反向代理和 HTTPS)。
客户端自己压缩会换到新会话。 客户端压缩或编辑历史后,网关把新历史保存到新的 OpenViking 会话。检索工具可用时,新历史开头的网关说明会告诉模型之前的会话在哪里。
实验性:Agent 自管上下文窗口
开启 Agent 自管上下文窗口(默认关闭)后,由模型自己决定何时开启新的上下文窗口,并自己写交接笔记,不必等网关生成摘要。网关会给模型两个工具,并在窗口快满时提醒它。这项设置只对能使用 OpenViking 工具的对话生效,其他对话照常压缩;开启压缩时,如果模型一直不开新窗口,压缩仍会兜底。
在 Studio 中的位置:上下文配置 → 长对话 → Agent 自管上下文窗口(标有实验性),以及软提醒时机(0.7)和硬提醒时机(0.85)。效果取决于所用的模型,后续版本中的行为也可能调整。
模型多了什么
两个额外的工具。它们不在上下文配置的工具清单里,只由这项设置控制。
| 工具 | 作用 |
|---|---|
openviking_new_context(reason, notes, next_steps) | 开启新窗口,next_steps 可省略。 |
openviking_context_remaining() | 返回窗口序号、已用和剩余 token 的估算值、本窗口内的用户轮数、距用户上一条消息的时间,以及一句建议。 |
openviking_new_context 必须在一轮里单独调用;和其他工具同一轮调用时,这一轮的每个调用都返回错误,窗口不重置。网关在本次请求里已经在同一位置替换过上下文(例如刚压缩过)时,它同样返回错误。
模型还会看到:
- 开头的网关说明里多一行,告诉模型它用这两个工具自己管理上下文窗口。
- 一行状态:每条新的用户消息之后,记忆块末尾附上
[context-status] window wN · ~X/Y tokens (P%) · T since your previous message。 - 软提醒:窗口用到软提醒时机(0.7)时,网关提醒一次,要求模型做完当前这一步就开启新窗口,并在交接笔记里记下之后要用的确切路径、行号和取值。
- 硬提醒:用到硬提醒时机(0.85)时,要求模型立即开启新窗口,此后每一步都重复,直到模型开启新窗口或压缩接手。
提醒附在新的用户消息上,工具步骤里则附在客户端回传的工具结果上;网关自己执行的 OpenViking 工具往返之间不加提醒。
模型开启新窗口时
模型调用 openviking_new_context 后,网关把目前为止的对话换成一个窗口头,模型在同一条回复里接着工作。窗口头依次包含:
- 这是模型开启的第 N 个窗口,内容不是用户写的;
- reason、notes 和 next_steps;
- 用户最新一条消息的原文;
- 检索之前窗口的方法(条件和压缩时相同);
- 这段对话开头的网关说明。
之后的请求会根据客户端的历史重建同一个窗口。重置之前的内容会立即保存到 OpenViking,之后仍可检索;reason 和 notes 只出现在窗口头里,不保存到 OpenViking。每次重置让服务商缓存失效一次,但不需要摘要请求。
在请求日志中,每条请求的上下文窗口一行会显示窗口序号(“第 N 个窗口,由模型管理”),模型开启了新窗口时显示“模型开启了新窗口”,还会显示模型收到了哪种提醒(“已提醒模型尽快开启新窗口”或“已要求模型立即开启新窗口”)。
下一步
- 长对话超出上下文长度:每种压缩失败原因的含义和处理方法。
- 模型:上游的上下文窗口和其他模型设置在哪里。
- 什么是上下文配置:上下文配置的其他分区及默认值。
- 上限:限制单次请求的工具轮数和 token。