Files

144 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 上下文超限自动恢复与压测记录
验证日期:2026-09-11。
## 行为
- 模型管理的 `max_input_tokens` 保持可空,不增加 32000 或任何默认模型窗口。明确配置或已有模型 profile 时做预裁剪;两者均无时,首个请求保持原样。
- 主代理、子代理的最终 `ContextWindowGuardMiddleware` 在模型明确返回超限时,对实际发送的请求副本缩短后重试,不重新执行外层摘要、RAG 或记忆注入。原始会话历史不改写。
- 最多首次调用加四次恢复调用。恢复预算来自失败请求的估算值,每次降到约 35%;最后尝试最小请求。30 秒用于限制继续发起恢复尝试,单次调用的超时仍由模型配置控制。
- 不盲目缩减已较小的输出额度,避免推理模型耗尽额度却没有正文。恢复后仍只有思考内容或空白时,普通问答返回自然澄清。
- 不发送空消息列表;不静默删除结构化输出格式和强制工具约束。必要约束无法满足时保留内部失败标记,供业务调用方识别,不能伪造成功 JSON 或已执行的操作。
- 摘要限制针对最终渲染的请求(包括 XML 和摘要提示词)。使用已有摘要裁剪配置与摘要模型上限中较小者,不新增默认模型窗口;`trim_tokens_to_summarize: null` 现在会正确传递。
- 摘要超限也缩短重试;摘要失败时保留可用旧摘要、最近消息和真实用户问题,不缓存失败文案、不阻断主问答。内部摘要调用隔离父级流式回调,避免摘要文字混入回答。
- 上下文错误识别兼容实测 MiniMax 的 `invalid params, context window exceeds limit (2013)`。普通鉴权、额度、网络等错误仍由原错误处理流程负责。
## 自动回归
以下命令从 backend 目录运行:
```powershell
.venv/Scripts/python.exe -m pytest tests/test_context_recovery.py tests/test_context_recovery_transport.py tests/test_llm_error_handling_middleware.py tests/test_summarization_human_message.py tests/test_summarization_multiturn.py tests/test_model_management.py tests/test_writing_mode_report_middleware.py tests/test_roundtable_role_boundary.py -q -p no:cacheprovider
```
首轮结果:135 passed;补充多轮问答后的最终结果:**168 passed**。
覆盖内容:
- 未填写输入上限时不擅自预裁剪,显式上限和绑定模型 profile 生效。
- 中英文、表情、单条超长输入;持续超限;输出预算;极小无效预算。
- 50 组固定随机种子的历史消息、工具调用/结果、隐藏上下文和系统提示组合,验证长度、工具配对与原始历史不变。
- 同步/异步恢复、取消、LangGraph 中断、重试时限、熔断半开探针释放。
- 摘要输入超限、摘要不可用、失败不污染缓存、摘要消息流不泄漏。
- 真实 `PatchedChatOpenAI` 客户端 + 模拟 HTTP 400/SSE + LangGraph 的端到端请求链,验证只返回一次正文、不重复上下文注入、不泄漏超限异常。
Python 改动与新增测试、探针脚本通过 Ruff 检查。
## 真实模型联调
使用当前配置中的 MiniMax-M2.7,仅发送自动生成的合成材料,不使用用户会话或业务文档。40000 行输入的本地估算从 **1,840,102** 降到 **644,001**;这些是保守估算值,不是供应商统计的实际 token 数。
| 模式 | 首次调用 | 第二次调用 | 可见正文 | 耗时 |
| --- | --- | --- | --- | --- |
| 普通调用 | 真实上下文超限 2013 | 自动缩短后成功 | 验证通过 | 18.83 秒 |
| SSE 流式 | 真实上下文超限 2013 | 自动缩短后成功 | 验证通过 | 21.26 秒 |
可重复运行的脚本(真实请求会消耗已配置模型额度):
```powershell
.venv/Scripts/python.exe scripts/probe_context_recovery.py --live --model MiniMax-M2.7 --rows 40000
.venv/Scripts/python.exe scripts/probe_context_recovery.py --live --model MiniMax-M2.7 --rows 40000 --stream
```
探针只在进程内设置 90 秒请求超时与 1024 输出预算,不修改配置文件。初期以较小输出预算测试时发现只有思考、没有正文的问题;30 秒网络超时测试也曾超时,扩大探针超时后完成上述验证。生产调用仍使用自身超时配置。
## 多轮问答追加验证
当前配置的主动摘要开关为 `summarization.enabled: false`,前端默认 `contextCompressionEnabled: false`。前端开启“上下文压缩”后,通过 `summarization_enabled` 传到后端。触发条件为 **120000 token 或 160 条消息**,保留目标为 40000 token。未开启主动摘要时,不会因为对话轮次增加而调用摘要模型;上下文超限后的裁剪重试仍可独立工作。本次没有修改这些开关或生产阈值。
多轮测试发现并修复:
1. 消息数达到阈值但历史尚未达到 token 保留目标时,提前返回导致不触发摘要。现在先执行保留窗口调整,再判断是否有历史可摘要。
2. 反复从全量旧历史重新摘要,旧事实可能被摘要输入截断删除。现在使用“上次摘要 + 新增历史”增量合并,并保留完整原始消息。
3. 摘要提示词与历史拼接后整体截断,可能保留提示词却删除历史开头。现在先计算提示词与已有摘要开销,再给历史分配预算。
4. 历史回复的思考文本、历史 token 用量缩放、摘要输出的思考文本可能干扰事实提取。现在清理这些信息的临时副本,存储的原始聊天记录不改写。
5. 摘要模型可能执行历史中的“只回复已记住”。现在明确区分摘要任务和历史数据,过滤纯确认回复;摘要失败时保留尚未成功摘要的事实,由最终 guard 按实际需要裁剪,不把确认文案当记忆。
6. 补齐 fraction 阈值、复合条件和绑定模型 profile 的多轮处理。
| 测试 | 真正生成摘要的轮次 | 结果 |
| --- | --- | --- |
| 当前阈值的 105 轮模拟,逐轮重建 agent | 81、101 | 210 条消息完整,早期事实可回忆 |
| 小摘要预算的 24 轮模拟 | 5、8、11、14、17、20、23 | 多次合并后仍保留首轮事实 |
| 12 轮工具问答,同步/异步分别运行 | 多次触发 | 每轮工具恰好执行一次,48 条消息完整,调用与结果不拆散 |
| 真实 MiniMax 12 轮,第一次最终复测 | 5、8、11 | 正确回忆代号、更新后的预算和期限;59.02 秒 |
| 真实 MiniMax 12 轮,第二次独立复测 | 5、8、11 | 再次通过相同事实检查;68.16 秒 |
真实测试为加快验证,仅在探针进程内把摘要触发调为 8 条消息、保留 2 条消息,并使用 2048 输出预算;**未设置默认输入上限,也未修改配置文件**。第 9 轮清空进程摘要缓存,检查能否从会话检查点恢复。第 6、7、9、10、12 轮复用摘要,不额外调用摘要模型。每次真正摘要均检查 checkpoint 中的摘要覆盖范围确实更新,而不只检查触发事件。
首轮提供代号 `ORBIT-42`、预算 35 万元和期限 `2026-12-20`,第 6 轮把预算改为 48 万元,第 8 轮把期限改为 `2026-12-28`。两次最终复测第 12 轮均正确返回:
```json
{"code":"ORBIT-42","budget":48,"deadline":"2026-12-28"}
```
复测过程中曾出现“摘要更新了,但答案事实错误”的失败,未计为通过;上述两次成功记录均来自清理历史思考及用量缩放后的版本。逐轮输入场景、正文、摘要正文和缓存更新记录保存在:
- [第一次最终复测](multiturn-probe-20260911.json)
- [第二次独立复测](multiturn-probe-20260911-repeat.json)
复现命令(使用合成材料,会调用已配置模型):
```powershell
.venv/Scripts/python.exe scripts/probe_multiturn_compression.py --live --output docs/multiturn-probe-20260911.json
```
## 前端及验证边界
- 模型管理页已注明输入上限可留空;该 TSX 文件单独转换通过。
- `pnpm typecheck` **未通过**:未改动的 `message-list-item.tsx`、`core/blog`(缺少 nextra)、`GeneralQA.tsx` 等存在类型或依赖错误。本次不把前端全量检查记为通过。
- 真实供应商测试覆盖 MiniMax-M2.7,不能等同于所有供应商均已实测。主代理/子代理和摘要调用以外的独立模型调用不因该中间件自动获得保护;标题生成原本已有本地标题降级。
- 极端情况下会丢失部分材料并请求用户补充;恢复机制不保证缺失信息时仍能完成原任务,也不掩盖鉴权失败、额度不足或服务不可用。
## 部署版本兼容修复(2026-09-11)
前面的本机测试使用 LangChain 1.3.15,未覆盖部署的 1.2.15,这是此前验证的遗漏。
已在真实 1.2.15 包上复现 `_trigger_clauses` AttributeError:旧基类只提供 `_trigger_conditions`。
现在应用自行保存和计算触发条件,不再读取这两个版本相关的私有属性;字典条件的 AND、条件列表的 OR 和旧参数映射保持一致。
此前新增的 `internal_call_transformer` 依赖已移除。没有要求升级部署依赖,没有引入默认输入上限。
本次将用户提供的全部 20 个包安装到隔离目录,以 PYTHONPATH 优先加载,未替换本机虚拟环境。
其余未提供版本的依赖沿用本机,测试主机为 Windows;不把该结果宣称为生产服务器完整环境的启动验证。
- 用户版本组合(LangChain 1.2.15、Core 1.3.2、LangGraph 1.1.9 等):177 项通过,26.26 秒。
- 本机新版组合:同样 177 项通过,24.64 秒。
- 覆盖 Gateway / 主代理导入、105 轮问答、重复压缩、缓存恢复、同步/异步工具和消息流隔离。
- 新增删除基类两个私有触发属性后仍能正常判断阈值的回归测试,涵盖 tuple、OR、AND、混合和未启用情况。
- 用户版本组合下真实 MiniMax-M2.7 12 轮问答:第 5、8、11 轮摘要,第 9 轮清空进程缓存后正常恢复;最终准确返回代号 ORBIT-42、更新预算 48、更新期限 2026-12-28。54.53 秒,无属性错误。
- 所有被调用的摘要中间件方法均已检查能在 1.2.15 下解析;Ruff 通过。
[用户版本组合的真实多轮记录及 20 个版本号](multiturn-probe-langchain-1.2.15.json)。
## 隐藏压缩提示与连续问答复测
后台仅记录真实摘要日志,不再向 custom 流发送 `context_compacting` 通知;前端也忽略旧后台的同名事件,不弹 toast。摘要开关和压缩功能保留。
新增模型每轮上报 150000 输入 token 的 12 轮测试,使用 2000 token 摘要触发阈值:同步和异步均仅在第 2 轮摘要,后续复用;历史用量数据未修改。该测试在用户的 1.2.15 依赖组合和本机新版均通过。部署组合相关回归共 118 项通过(22.28 秒)。
尚未拿到用户连续弹提示时的后台 cache / counted_tokens / msgs 日志,未复现该会话的每轮重复摘要,不能把隐藏提示当作已修复重复摘要根因。
## 修复压缩后每轮重新摘要
已用过长摘要复现另一条真实缺陷路径:摘要加保留内容在生成当时就超过阈值,旧逻辑之后每轮都会把同一个超阈值基础内容再次送摘要模型。12 轮测试中第 2~12 轮均调用摘要。此前的简短摘要测试没有覆盖此边界;尚无生产会话日志,不能据此断言这是用户会话唯一原因。
修复:缓存可用且上次压缩结果本身就超阈值时,要求**压缩后新增内容**累计达到原触发阈值的 25% 才再摘要。正常压缩后低于阈值的路径保持原判定。这个比例用于避免无效重复摘要,不是模型输入上限;最终 ContextWindowGuard 的裁剪及超限重试继续工作。子集计数移除原请求 usage_metadata 的缩放影响,只修改临时副本。日志新增 waiting_for_growth,并区分 unusable 与 stale 缓存。
用户提供的 1.2.15 等 20 个包组合最终回归 **187 passed,26.59 秒**:
- 12 轮过长摘要 + 短追问:仅第 2 轮摘要,修复前为第 2~12 轮;同步/异步均通过。
- 36 轮持续增加材料:第 2、19、36 轮摘要;每轮清空进程缓存,仍可由 checkpoint 恢复,不是直接禁用后续摘要。
- 超长摘要 + 严格 800 token 测试模型 + 连续 8 轮:分别验证配置上限(预裁剪,无拒绝)和不配置(真实抛出 context_length_exceeded 后缩短重试),同步/异步四种组合均正常回答最新问题;原会话消息数完整。这里使用可控测试模型,未宣称所有供应商均实测。
- 既有 105 轮、工具执行、首次阈值、缓存恢复、旧版导入及摘要流隔离回归继续通过。Ruff 通过。