辅助压缩调用返回空响应导致 Hermes 网关崩溃 / Auxiliary compression empty response crashes Hermes gateway

张开发
2026/4/19 7:29:40 15 分钟阅读

分享文章

辅助压缩调用返回空响应导致 Hermes 网关崩溃 / Auxiliary compression empty response crashes Hermes gateway
辅助压缩调用返回空响应导致 Hermes 网关崩溃 / Auxiliary compression empty response crashes Hermes gateway作者: cosmoslife日期: 2026/04/18 14:30:00博文链接: https://blog.csdn.net/cosmoslife仓库: https://github.com/NousResearch/hermes-agent创建时间: 2026-04-18 |关闭时间: 未关闭修复 PR: 无(尚未修复)关联 Issue: #11914(同类根因:压缩失败后静态文本注入)、#11906(空文本内容块致 HTTP 400)关联 PR: #11929(_build_api_kwargs空文本消毒防御)问题概述用户使用glm-5.1通过自定义 provider(cmkey.cn/v1)运行 Hermes Agent v0.10.0,当对话触发上下文压缩时,辅助压缩模型返回空响应。Hermes 的 fallback 模型(glm-5)同样返回空。两次调用均未产生有效摘要,导致网关进入异常状态并最终崩溃。这是一个辅助客户端 provider 兼容性 + 压缩 fallback 链脆弱性的复合问题:自定义 OpenAI 兼容端点在特定 prompt 模式下返回空 content,而压缩流程对空响应的容错不足。复现路径配置config.yaml:model provider=custom,base_url=https://cmkey.cn/v1,default=glm-5.1配置 auxiliary.compression:provider=custom,model=glm-5.1,base_url=https://cmkey.cn/v1正常对话直到上下文超过阈值(默认 85%)压缩触发 →call_llm(task="compression")→ cmkey.cn 返回空 contentfallback 到 glm-5 → 同样返回空_generate_summary返回 None → 插入静态 fallback 文本但后续操作中空 content 累积,最终网关崩溃根因分析置信度: 🟡中(cmkey.cn 为第三方代理,无法直接检查其端行为;Hermes 侧的代码路径已完整追踪)触发条件用户配置自定义 OpenAI 兼容端点作为辅助压缩 provider对话上下文超过压缩阈值(默认 85% context length)自定义端点对压缩 prompt 返回content: ""或content: null执行路径追踪对话超过阈值 ↓ context_compressor.py:compress() [L999] ↓ 检查 should_compress() ↓ 分离 head/middle/tail 消息 ↓ context_compressor.py:_generate_summary() [L551] ↓ 构建 summary prompt(~670行结构化 prompt) ↓ 调用 call_llm(task="compression", ...) ↓ auxiliary_client.py:call_llm() [L2410] ↓ _resolve_task_provider_model() 解析 provider/model ↓ _get_cached_client() 获取 OpenAI client ↓ client.chat.completions.create(**kwargs) ↓ *** cmkey.cn 返回 response,但 choices[0].message.content = "" 或 null *** ↓ context_compressor.py:_generate_summary() [L700-704] ↓ content = response.choices[0].message.content ← 空字符串或 None ↓ summary = content.strip() ← 空字符串 ↓ return self._with_summary_prefix(summary) ← 只返回 SUMMARY_PREFIX ↓ context_compressor.py:compress() [L1088-1096] ↓ if not summary: ← 空字符串被视为 falsy ↓ 插入静态 fallback 文本 ↓ 后续轮次:网关累积异常状态 → 崩溃代码位置角色文件函数/模块关键行压缩入口agent/context_compressor.pycompress()L999摘要生成agent/context_compressor.py_generate_summary()L551LLM 调用agent/auxiliary_client.pycall_llm()L2410响应验证agent/auxiliary_client.py_validate_llm_response()L2379静态 fallbackagent/context_compressor.pycompress()L1088空文本消毒run_agent.py_build_api_kwargs()(PR #11929)缺陷本质问题出在三层防御的缺口:缺口 1:_generate_summary不区分"空摘要"和"调用失败"# agent/context_compressor.py:L700-704content=response.choices[0].message.contentifnotisinstance(content,str):content=str(content)ifcontentelse""summary=content.strip()当content为空字符串时,summary = "",函数返回_with_summary_prefix(""),结果为只有前缀的空摘要。但调用者compress()中:# agent/context_compressor.py:L1088ifnotsummary:# 插入静态 fallback空摘要被正确识别为 falsy 并触发 fallback。但问题在于:如果自定义端点返回的不是空字符串,而是" "(空白)或某个无意义的短字符串(如"ok"),summary会被视为 truthy,一个无用的"摘要"会被注入到上下文中,后续对话质量严重下降。缺口 2:fallback 模型使用同一 provider# agent/context_compressor.py:L736-742if_is_model_not_foundandself.summary_modelandself.summary_model!=self.model:self._summary_model_fallen_back=True

更多文章