OpenCode v2.2.5 的更新没有引入一个醒目的新工作台,却集中修补了长会话最难处理的几类故障:上下文余量可以按比例预留,LiteLLM 的上下文窗口错误会触发 Compaction,切换模型或恢复中断响应时不再把推理内容降级成普通文本,MCP stdio 也能处理超过 10 MiB 的消息。

这些变化共同指向一个问题:Agent 能否在输入规模、模型窗口、工具输出和连接状态不断变化时,仍然保持可恢复、可观察、可解释。本文基于截至 2026 年 11 月 5 日的 OpenCode v2.2.5 官方 Changelog,给出一套面向团队的上下文韧性方案。文中的阈值与流程是工程建议,具体配置应以当前版本 Schema、模型元数据和真实压测结果为准。

一、长会话的核心风险不是“内容太多”

很多团队把上下文溢出理解为一次简单的超长输入:删掉几段历史,再重新发送即可。真实生产环境更复杂,因为一次模型请求通常同时携带系统指令、会话历史、工具定义、工具结果、附件元数据、推理内容和当前用户输入。

可用输入预算 = 模型上下文窗口
             - 输出预留
             - Compaction Buffer
             - 协议与工具固定开销

如果只统计聊天气泡的字符数,就会低估工具 Schema、图片引用、代码差异和 MCP 返回值。更麻烦的是,不同 Provider 对输入上限、输出上限和错误类型的表达并不完全一致。同一个会话切换模型后,原来安全的历史可能立即变得不可发送。

风险来源表面现象真正需要保护的对象
历史持续增长请求突然报超限当前任务目标与关键决策
工具一次返回过大MCP 断连或模型拒绝结构化结果与可追溯引用
切换到较小窗口模型续跑失败推理结构和消息语义
Provider 报错口径不同自动恢复没有触发错误分类与恢复状态机
压缩过于激进会话能跑但答案漂移不变量、验收条件与证据

因此,“不报错”不是韧性。真正的目标是:接近容量边界时提前收敛;发生可恢复错误时只重试必要步骤;压缩后仍保留任务状态;无法安全继续时明确暂停,而不是悄悄遗忘约束。

二、v2.2.5 到底改变了什么

2.1 百分比 Compaction Buffer

v2.2.5 允许 compaction.buffer 使用模型上下文窗口的百分比,例如 20%,同时继续兼容固定 Token 数。百分比配置的意义,是让同一份团队配置适配不同窗口大小的模型,而不必为每个模型复制一套绝对值。

{
  "$schema": "https://opencode.ai/config.json",
  "compaction": {
    "buffer": "20%"
  }
}

假设模型 A 的窗口为 32k,模型 B 为 128k,20% 会分别预留约 6.4k 与 25.6k 的窗口空间。固定 8192 Token 在模型 A 上可能过于保守,在模型 B 上又可能不足。百分比更适合模型池,固定值则适合窗口稳定、成本模型明确的单一模型。

2.2 三个与恢复有关的修复

官方 Changelog 还记录了三项直接影响稳定性的修复:Compaction Prompt 会在“输入上限大于上下文窗口”时按真实窗口封顶;LiteLLM 的上下文窗口错误会触发 Compaction,而不是直接终止会话;切换模型或恢复中断响应时,推理内容不再被转成普通文本。

三者分别修复容量计算、错误分类和消息语义。它们不是同一个开关,也不能互相替代:预算算错会让压缩来得太晚,错误分类不准会让可恢复故障变成终止,消息类型丢失则可能让 Provider 拒绝续接请求。

2.3 MCP 大消息与生命周期修复

v2.2.5 对 MCP stdio 做了两项关键修复:超过 10 MiB 的消息不再因此断开,并显著提高大消息读取速度;空闲 Location 关闭 MCP Server 后,也不会立刻把它重新拉起。

这意味着传输层上限提高了,但不意味着应该把数据库全表、完整构建日志或巨型代码索引原样塞进模型上下文。MCP 能搬运大报文,只解决“能不能到达”;Agent 是否应该消费、怎样裁剪、如何审计,仍属于应用治理。

三、百分比与固定 Token 应该怎么选

3.1 模型池优先使用百分比

当团队允许在 32k、64k、128k 等多种模型之间切换时,百分比更容易维持一致的安全边界。一个保守起点可以是 15% 到 25%,然后根据压缩频率、平均输出长度和工具结果规模调整。

{
  "compaction": {
    "buffer": "20%"
  }
}

这里的 20% 不是官方推荐值,而是示例。分析型任务可能需要较长输出,代码修复任务可能产生巨大的 Diff 与测试日志,二者不应机械共享同一阈值。

3.2 单模型、强 SLA 场景可使用固定值

如果生产环境锁定单一模型,而且最大输出、工具集合与请求结构都经过压测,固定 Token 更容易做容量审计。

{
  "compaction": {
    "buffer": 16384
  }
}

固定值的优点是可预测,缺点是迁移模型时容易失真。升级模型或调整 Provider 后,应把窗口元数据、最大输出与 Buffer 作为同一个变更单处理。

3.3 先测量再定阈值

可以用下面的近似式建立初始预算:

Buffer >= P99(单轮新增输入)
        + 最大计划输出
        + 工具定义与协议抖动余量

P99(单轮新增输入) 应包含用户输入、并发工具结果和附件引用。若某个 MCP 工具偶尔返回 8 MiB JSON,单纯调高 Buffer 并不是好方案,应该先给工具增加分页、字段选择、聚合或外部制品引用。

四、建立可恢复的 Compaction 状态机

4.1 压缩前保存任务不变量

高质量 Compaction 不是“把前文缩短”,而是把会话转换为可继续执行的状态。建议摘要至少保留六类信息:目标、已确认事实、用户约束、已产生副作用、验证证据和未完成步骤。

objective: 修复订单导入的重复写入
invariants:
  - 不改变公开 API 响应结构
  - 不删除生产数据
decisions:
  - 使用幂等键 order_no + source
evidence:
  - 单元测试 28/28 通过
side_effects:
  - 尚未提交代码、尚未部署
next_step:
  - 运行并发导入测试并检查唯一索引冲突

如果摘要只保留“继续修复订单问题”,模型虽然能继续说话,却不知道哪些选择已被否决、哪些操作已经执行,重复副作用的风险反而更高。

4.2 区分预防性压缩与错误后恢复

预防性压缩发生在预算达到阈值之前,通常可以完整读取当前状态并生成摘要;错误后恢复则发生在 Provider 已拒绝请求之后,需要保证重试不会重复执行上一轮工具副作用。

预算接近阈值 -> 生成摘要 -> 校验摘要 -> 继续请求

Provider 上下文错误 -> 分类错误 -> 确认上一轮副作用状态
                    -> 执行 Compaction -> 仅重放模型请求

“仅重放模型请求”很重要。若上一轮已经调用了发消息、提交、付款或删除工具,不能因为模型响应丢失就重新调用工具。应先查询目标系统的实时状态,再决定是续接结果还是人工介入。

4.3 给恢复过程设置上限

自动恢复必须有熔断条件。一个实用策略是:同一请求最多自动压缩并重试一次;如果仍然报上下文错误,暂停会话并输出当前模型、估算输入、已执行工具和剩余步骤。

if (error.kind === "context_window" && attempts === 0) {
  await compactSession()
  return retryModelRequest()
}

return pauseWithEvidence({
  reason: "context recovery exhausted",
  attempts,
  lastToolSideEffect
})

无限压缩可能逐轮丢失事实,还会隐藏真正的窗口元数据错误。恢复的目的不是“永远跑下去”,而是在证据充分时安全续跑。

五、LiteLLM 场景要特别检查错误分类

5.1 为什么代理层容易让恢复失效

LiteLLM 处在 OpenCode 与上游模型之间,可能统一路由多个 Provider。不同上游对上下文溢出的 HTTP 状态码、错误文本和字段结构不一致;代理层如果没有保留可识别语义,客户端就可能把容量错误当成普通请求失败。

v2.2.5 已修复 LiteLLM 上下文窗口错误不会触发 Compaction 的问题。升级后仍应做契约测试,因为企业网关、二次封装或旧版代理可能改变错误体。

5.2 用可控输入验证恢复链路

不要等线上会话首次溢出才验证。可以在测试模型上构造接近窗口的输入,检查日志中是否出现“识别上下文错误—执行 Compaction—重新请求—成功或明确熔断”的完整链路。

用例 A:输入低于阈值,不应压缩
用例 B:输入接近阈值,预防性压缩一次
用例 C:代理返回标准上下文错误,压缩后成功
用例 D:代理返回未知错误,不得误判为上下文错误
用例 E:压缩后仍超限,只重试一次并暂停

验证时记录错误分类,不要保存包含业务数据的完整 Prompt。生产日志可以保留请求大小、模型标识、错误类别、恢复次数和会话哈希。

六、切换模型时保护消息语义

6.1 推理内容不是普通文本

部分 Provider 使用专门的消息块保存 Reasoning。若在切换模型或恢复中断响应时把它展平为普通文本,新 Provider 可能拒绝格式,也可能把内部推理当作用户可见内容重新解释。v2.2.5 修复了这类转换问题。

团队仍应把模型切换视为一次兼容性边界:检查目标模型窗口、支持的内容块、工具调用格式、图片与 PDF 能力,而不是只替换模型名称。

6.2 切换前执行四项检查

1. 目标模型窗口是否小于当前估算输入;
2. 当前会话是否包含目标模型不支持的媒体或工具块;
3. 是否存在正在进行、尚未确认结果的工具调用;
4. 是否需要先 Compaction,再切换模型。

对于关键任务,建议在切换点生成一份结构化 Checkpoint,并记录“由哪个模型切换到哪个模型、为何切换、切换前最后一个已确认结果”。这样后续质量回退时能定位原因。

七、MCP 超过 10 MiB 后,治理重点反而更清晰

7.1 传输成功不等于上下文可用

v2.2.5 让 MCP stdio 能稳定读取超过 10 MiB 的消息,这是兼容性与性能改进。大报文进入 OpenCode 后,仍可能在序列化、UI 展示、会话存储和模型输入阶段造成压力。

层次可能瓶颈推荐控制
MCP 传输stdio 帧与读取速度升级 v2.2.5,监控消息字节数
工具协议单次结果过大分页、过滤、字段投影
会话存储历史膨胀保存摘要与外部引用
模型上下文Token 超限预聚合、检索片段、Compaction
审计系统敏感数据复制脱敏、哈希、保留期

大报文能力最适合承载必须完整传输但不一定全部进入模型的内容,例如构建产物清单、搜索索引结果或大型诊断包。应用层应先解析,再选择与当前问题相关的片段。

7.2 让 MCP 工具支持查询收敛

一个容易失控的工具接口是 get_all_logs();更合理的接口应支持时间范围、级别、服务、游标和最大条数。

{
  "service": "order-api",
  "level": ["ERROR", "WARN"],
  "from": "2026-11-05T08:00:00+08:00",
  "to": "2026-11-05T09:00:00+08:00",
  "fields": ["timestamp", "traceId", "message"],
  "limit": 500,
  "cursor": null
}

返回值还应包含 total、nextCursor、truncated 和原始数据的外部引用。Agent 看到 truncated: true 时可以继续缩小条件,而不是误以为已经拿到全集。

7.3 二进制与巨型文本优先使用制品引用

对于压缩包、图片、Heap Snapshot 或完整日志,最好把内容存入受控对象存储或 Artifact Provider,只把校验和、大小、权限范围和临时访问标识返回给 Agent。

{
  "artifactId": "diag_20261105_001",
  "sha256": "<content-hash>",
  "bytes": 18432000,
  "contentType": "application/gzip",
  "expiresAt": "2026-11-06T09:00:00+08:00",
  "summary": "order-api error logs and thread dump"
}

这样可以避免把不可读的原始数据复制进每轮会话,同时保留审计与按需读取能力。

八、MCP 生命周期也要进入稳定性设计

8.1 避免空闲关闭后立即重启

v2.2.5 修复了空闲 Location 关闭 MCP Server 后立即重启的问题。旧行为可能造成进程抖动、重复初始化、认证请求增加和日志噪声。升级后应观察空闲关闭、再次调用和工作区切换三个场景。

活跃调用 -> 空闲计时 -> 正常关闭
                    -> 无新请求:保持关闭
                    -> 新请求:按需启动并完成健康检查

如果 MCP Server 启动成本很高,可以在服务端实现连接池或缓存,但不要通过无限保活掩盖生命周期缺陷。缓存必须绑定租户、权限和数据版本,避免跨会话泄露。

8.2 启动与重连必须幂等

MCP Server 启动脚本不应每次重连都重复迁移数据库、注册 Webhook 或创建账号。把初始化分成“只读能力探测”和“显式管理动作”,让客户端重连只恢复连接,不产生业务副作用。

进程启动:加载配置 -> 校验凭证 -> 暴露 capabilities
首次调用:建立所需连接池
管理变更:独立工具 + 明确权限 + 审计记录

九、把可观测性建立在元数据而不是原文上

9.1 最小监控指标

要判断配置是否有效,至少需要观察以下指标:

session_input_tokens_estimate
compaction_trigger_total{reason}
compaction_duration_ms
context_error_total{provider,model}
context_recovery_total{result}
mcp_message_bytes{server,tool,direction}
mcp_restart_total{reason}
model_switch_total{from,to,result}

其中 reason 要区分预防性阈值、Provider 上下文错误、手动操作和模型切换。只有总次数,没有原因分布,无法判断是 Buffer 太小、工具输出失控,还是模型元数据错误。

9.2 日志默认不记录完整上下文

上下文与 MCP 结果往往包含源代码、客户数据、令牌或内部地址。推荐记录大小、类型、哈希、工具名、会话匿名标识和恢复结果;需要排障原文时,使用短保留期、受控访问与脱敏采样。

{
  "event": "context_recovery",
  "sessionHash": "s_7f2c...",
  "model": "approved/model-a",
  "estimatedInputTokens": 98765,
  "buffer": "20%",
  "reason": "provider_context_window",
  "attempt": 1,
  "result": "resumed"
}

十、上线前做一组故障演练

10.1 容量与模型切换演练

准备一个包含长历史、多次工具结果和代码差异的测试会话,分别在大窗口模型与小窗口模型上运行。验证百分比 Buffer 是否按各自窗口计算,切换后是否先压缩,关键任务约束是否仍在。

验收不应只看“最终回答成功”,还要检查摘要中是否保留不变量、已执行副作用和下一步。如果任务要求不得修改 API,但压缩后丢失了这一条,即使请求成功也算演练失败。

10.2 LiteLLM 与网络异常演练

通过测试代理返回标准上下文错误、普通 400、429 和短暂 5xx,确认只有可恢复的上下文错误触发 Compaction;限流遵守退避策略;未知 400 不被盲目重试;5xx 重试不会重复调用有副作用的工具。

上下文错误 -> Compaction -> 重试一次
429        -> 按 Retry-After 退避
未知 400   -> 停止并输出错误证据
5xx        -> 查询工具副作用状态后决定是否续接

10.3 MCP 大报文与生命周期演练

构造 1 MiB、10 MiB、20 MiB 三档 JSON 结果,记录传输耗时、内存、UI 响应和最终进入模型的 Token。然后让 Location 进入空闲状态,确认 MCP Server 正常关闭且不会无请求自启;再次调用时只启动一次并恢复服务。

十一、推荐的渐进式上线顺序

第一阶段只升级到 v2.2.5,不改变 Buffer,先观察上下文错误与 MCP 报文分布;第二阶段在测试组启用百分比 Buffer,并为高风险工具增加结果大小上限;第三阶段完成 LiteLLM 恢复演练和模型切换契约测试;第四阶段才向全团队推广。

rollout:
  phase_1: upgrade_and_observe
  phase_2: percent_buffer_canary
  phase_3: recovery_and_mcp_drills
  phase_4: organization_default
rollback:
  - restore_previous_buffer
  - disable_oversized_tool_paths
  - pin_last_verified_client_version

每个阶段都要定义回滚点。百分比 Buffer 导致压缩频率异常、摘要质量下降或成本上升时,应先回退配置并分析,而不是直接把比例调到更小。

十二、常见误区

12.1 “窗口更大就不需要 Compaction”

更大窗口只能推迟边界,不能消除无关历史、工具噪声与模型切换问题。长上下文还可能提高延迟和成本。Compaction 的目标是保存任务状态,不只是适配小模型。

12.2 “MCP 能传 10 MiB 以上,就可以返回所有数据”

传输上限不是产品接口设计。分页、聚合、过滤和字段投影仍然是默认能力,大报文应该是少数受控场景。

12.3 “自动恢复成功就不用记录”

频繁自动恢复可能掩盖错误窗口元数据、失控工具或错误路由。成功恢复也要计数,并观察同一模型、工具和工作区是否聚集异常。

12.4 “压缩摘要由模型生成,所以无法验收”

摘要可以做结构校验:必须包含目标、不变量、已执行副作用、证据和下一步;关键标识可与会话状态比对;高风险任务还可以在压缩后要求人工确认 Checkpoint。

十三、一份可执行的验收清单

发布前可以逐项确认:

[ ] CLI、Desktop、Server 与 SDK 已对齐到验证过的版本
[ ] compaction.buffer 使用合法的百分比或 Token 数
[ ] 已验证至少两种上下文窗口的模型
[ ] LiteLLM 上下文错误能够触发一次 Compaction 恢复
[ ] 未知错误不会被误判并无限重试
[ ] 模型切换保留消息类型与任务 Checkpoint
[ ] MCP 工具支持分页、过滤或外部制品引用
[ ] 超过 10 MiB 的测试消息可传输且不会全量灌入模型
[ ] 空闲 MCP Server 不会自动重启抖动
[ ] 监控只记录必要元数据,原文按需受控采样
[ ] 所有有副作用工具都具备状态查询与幂等策略
[ ] 恢复失败时能暂停并输出可操作证据

十四、结语

OpenCode v2.2.5 的价值,在于把几个看似独立的边界问题连成了更可靠的恢复链:百分比 Buffer 让容量策略适配模型池,LiteLLM 错误识别让可恢复故障进入 Compaction,推理结构修复保护模型切换,MCP 大报文与空闲生命周期修复则提升工具链稳定性。

但版本升级只提供机制。生产可用仍取决于团队是否控制工具结果规模、保存任务不变量、区分可重试与不可重试错误、验证外部副作用,并用故障演练证明恢复链真正有效。一个成熟的 Agent 系统,不是永远不触碰边界,而是在接近边界时知道该压缩什么、该重试什么,以及何时必须停下来交还给人。

参考资料:

Logo

更多推荐