hermes agent 出现 Empty response 原因和解决方案:从 fallback providers 到 GLM stream 的排查路径

发布时间:2026/10/12 7:16:05
hermes agent 出现 Empty response 原因和解决方案:从 fallback providers 到 GLM stream 的排查路径 1. 先复现Empty response 到底卡在哪一层你看到的那行日志大概率长这样Empty response (no content or reasoning)后面紧跟No fallback providers configured。很多人第一反应是 Hermes Agent 崩了其实不是。HTTP 状态码是 200请求成功返回但response.content是空字符串reasoning字段没有tool_calls也没有。框架判断「没内容 失败」于是自动重试连续 3 次都空最后抛出 fallback 缺失的错误。这个链路里Hermes Agent 本身只做了两件事发请求、判断内容是否为空。真正出问题的是它调用的模型服务端。我用 GLM-4.5-air 跑本地 Agent 时就遇到过前十分钟对话正常后面突然连续空响应重启进程又恢复。这种「偶发 重启恢复」基本可以锁定是 provider 侧临时不稳定而不是你本地环境坏了。要定位问题第一步是拿到原始响应而不是看 Hermes 包装后的日志。你可以在 provider 调用处加一行打印import json raw client.chat.completions.with_raw_response.create( modelglm-4.5-air, messagesmessages, streamFalse, ) print(status:, raw.status_code) print(body:, raw.text)如果打印出来是{id:...,choices:[]}或者{content:null}那就确认了服务端返回了空壳。这时候问题不在 Hermes而在模型通道。接下来要做的就是把 endpoint 切到一个稳定的统一通道做对照测试同时补上 fallback providers让单点故障不再直接杀死整个 Agent。这一步的核心检索词是 hermes agent Empty response 排查适合所有在本地调试多模型 Agent、遇到空响应却不知道从哪下手的人。下面我会按「复现报错 → 切换 fallback → 确认返回非空」三步走把可复制的配置和验证命令都给出来。2. 前置把 endpoint 统一到 TaoToken 通道在配 fallback 之前先解决一个更底层的问题你的 provider 配置是不是散落在多个地方每个模型一套 Key、一套 Base URLGLM 用智谱的地址OpenAI 用另一套DeepSeek 再一套。这种配置方式在单模型时没问题一旦要做 fallback 切换鉴权和 endpoint 不一致就会导致「切过去了但 401」或者「切过去了还是空」。我的做法是把所有模型请求收敛到一个统一通道。TaoToken 提供 OpenAI 兼容的 API 入口Base URL 填https://taotoken.net/apiKey 用同一个模型 ID 按需切换。这样 fallback 配置里只需要改 model 字段不用维护多套鉴权。先拿 Key。打开https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys创建一个 API Key复制出来。注意这个 Key 只在创建时显示一次丢了就重新建。然后确认你的 Hermes Agent 用的是 OpenAI SDK 兼容层。大多数 Agent 框架的 provider 配置长这样providers: - name: taotoken base_url: https://taotoken.net/api api_key: ${TAOTOKEN_API_KEY} models: - glm-4.5-air - deepseek-chat - gpt-4.1-mini环境变量里设置export TAOTOKEN_API_KEYsk-你的key这里有个坑要注意有些框架的base_url要求带/v1有些不带。TaoToken 的兼容层用https://taotoken.net/api即可SDK 会自动补路径。如果你填了/v1反而可能 404。实测下来OpenAI Python SDK 1.x 版本直接填https://taotoken.net/api就能通。配好之后先别急着跑 Agent用一条 curl 验证通道是否正常curl https://taotoken.net/api/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: glm-4.5-air, messages: [{role: user, content: 回复一个字好}], stream: false }如果返回的 JSON 里choices[0].message.content是「好」说明通道没问题。如果这里就空了那问题在通道层不在 Hermes。这一步是整个排查的分水岭先确认单次请求能拿到非空内容再去配 fallback。3. 可复制配置fallback providers 与 stream 开关现在进入核心配置。Hermes Agent 的 fallback 机制通常写在 provider 配置里格式各框架略有差异但结构一致primary 一个fallbacks 一个列表按顺序尝试。下面这份 JSON 可以直接改路径后使用假设你的配置文件在~/.hermes/providers.json{ primary: { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: glm-4.5-air, stream: true, timeout: 30 }, fallbacks: [ { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: deepseek-chat, stream: false, timeout: 30 }, { provider: taotoken, base_url: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model: gpt-4.1-mini, stream: false, timeout: 30 } ], retry: { max_attempts: 3, empty_response_as_failure: true } }几个关键点。第一primary 的stream设为true因为 GLM 在流式模式下如果 chunk 丢失Hermes 会判定为空但 fallback 我全部设成stream: false原因是非流式请求拿到的是一次性完整响应更容易判断 content 是否为空也避开了 SSE 中断导致的假空。第二empty_response_as_failure必须为true否则空响应不会被当成失败fallback 永远不会触发。第三所有 fallback 共用同一个base_url和 Key这样切换时不会因为鉴权不一致而 401。如果你用的是 TOML 格式比如某些 Rust 写的 Agent等价配置[primary] provider taotoken base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model glm-4.5-air stream true timeout 30 [[fallbacks]] provider taotoken base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model deepseek-chat stream false timeout 30 [[fallbacks]] provider taotoken base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model gpt-4.1-mini stream false timeout 30stream 开关的对照逻辑是这样的GLM 在stream: true下最容易出现「HTTP 200 但 choices 为空」因为流式响应分多个 chunk如果服务端在推理中途卡死客户端可能只收到 role 声明就断了。改成stream: false后服务端要么返回完整内容要么返回空壳判断更干脆。我实测把 GLM 的 stream 关掉后空响应概率明显下降但偶尔还是会有所以 fallback 仍然必要。配置改完记得重启 Agent 进程很多框架不会热加载 provider 配置。重启后看日志里有没有Loaded 2 fallback providers之类的字样确认配置生效。4. 三步验证复现报错、切换 fallback、确认非空配置写好了不代表问题解决了得按顺序验证。这三步是我踩过坑之后固定下来的流程缺一步都可能误判。第一步复现报错。故意把 primary 的 model 改成一个不存在的 ID比如glm-4.5-air-notexist然后发一条请求。预期结果是 primary 失败自动切到第一个 fallbackdeepseek-chat最终返回非空内容。如果日志里出现No fallback providers configured说明你的 fallback 列表没被读到检查配置文件路径和格式。这一步验证的是 fallback 链路本身通不通。# 触发一次请求观察日志 python -c from hermes import Agent agent Agent(config~/.hermes/providers.json) resp agent.chat(你好) print(content:, repr(resp.content)) print(provider_used:, resp.provider) 预期输出类似content: 你好有什么可以帮你的吗 provider_used: deepseek-chat如果provider_used还是 primary说明 fallback 没触发回去检查empty_response_as_failure是否为 true。第二步切换 fallback 顺序。把 deepseek-chat 和 gpt-4.1-mini 调换位置再跑一次。确认provider_used跟着变。这一步验证的是 fallback 列表的顺序生效不是写死的。第三步确认返回非空。把 primary 改回glm-4.5-air正常发请求连续发 10 次统计有多少次走了 fallback。如果 10 次里超过 3 次 fallback说明 GLM 通道确实不稳定可以考虑把 primary 直接换成 deepseek-chatGLM 降为 fallback。import time empty_count 0 fallback_count 0 for i in range(10): resp agent.chat(f测试第{i}次) if not resp.content: empty_count 1 if resp.provider ! glm-4.5-air: fallback_count 1 time.sleep(1) print(f空响应: {empty_count}/10, fallback触发: {fallback_count}/10)这个统计跑下来你就能判断到底是 GLM 偶发抽风还是稳定不可用。偶发的话保留 fallback 就行稳定的话直接换 primary。5. 常见报错对照401、local proxy failed、reading choices排查过程中会遇到几个典型报错这里逐个对照。401 Unauthorized切 fallback 后出现 401九成是 fallback 的 Key 和 primary 不一致或者环境变量没读到。检查api_key_env指向的变量名是否和export的一致。如果你用的是 TaoToken 统一 Keyprimary 和 fallback 应该共用同一个环境变量不会出现这个问题。出现 401 时先跑一遍第 2 节的 curl确认 Key 本身有效。local proxy failed / connection refused这个报错通常出现在你本地配了代理但 Agent 进程没继承代理环境变量。注意这里说的是本地网络配置问题不是让你去搞什么特殊网络工具。解决办法是确认HTTP_PROXY/HTTPS_PROXY环境变量在启动 Agent 的 shell 里已经设置或者干脆在 provider 配置里指定base_url为直连地址。TaoToken 的https://taotoken.net/api在正常网络环境下可直接访问不需要额外代理配置。Error reading choices / choices is empty这是最接近 Empty response 的报错区别在于它在解析阶段就炸了而不是解析完发现 content 为空。原因通常是服务端返回了{choices:[]}SDK 尝试读choices[0]时越界。解决办法和 Empty response 一样配 fallback同时把 stream 关掉。另外检查一下你的 SDK 版本老版本 OpenAI SDK 对空 choices 的处理是直接抛异常新版本会返回空 content。升级到 1.x 以上会更好排查。OAuth / token expired如果你用的是需要 OAuth 的 providertoken 过期也会表现为空响应或 401。TaoToken 用的是静态 API Key不涉及 OAuth 刷新所以配好 Key 之后不会遇到这个问题。如果你同时混用了其他需要 OAuth 的通道建议把它们的刷新逻辑单独处理不要让 OAuth 失败污染 fallback 链路。stream 模式下 chunk 丢失日志里可能看到SSE connection closed unexpectedly或者incomplete chunk。这就是第 3 节说的 stream 问题。把该 provider 的stream设为false或者把timeout从 30 调到 60给慢推理更多时间。GLM-4.5-air 在长上下文下推理时间可能超过 30 秒超时后连接断开客户端收到空 chunk。排查顺序建议先看 HTTP 状态码401 查 Key200 但空查 choices连接失败查网络配置流式中断查 stream 开关。按这个顺序走基本能覆盖 90% 的 Empty response 场景。6. 把通道固定下来统一 Key 与模型切换走到这里你应该已经能稳定复现和定位 Empty response 了。最后一步是把这套配置固定成日常可用的形态避免每次换模型都重新折腾鉴权。核心思路是所有模型走同一个 Base URL 和同一个 Key模型差异只体现在model字段。这样 fallback 配置里不需要为每个 provider 单独维护 Key切换时也不会因为鉴权不一致而失败。TaoToken 的 API 入口https://taotoken.net/api支持 OpenAI 兼容协议GLM、DeepSeek、GPT 系列都可以通过同一个 Key 调用模型 ID 按需填。如果你要长期跑编码类 Agent可以考虑用 Coding Plan 把额度固定下来入口在https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan。日常调试模型返回是否正常用模型对话页面直接发一条消息就能验证地址是https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodel_chat。接入细节和参数说明在文档里https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc。回到 Empty response 这个问题本身最实用的经验是不要等 Agent 报错才去配 fallback而是在第一次接入模型时就把 fallback 列表写好。GLM 空响应、stream 中断、choices 为空这些都不是 Hermes 的 bug而是多模型环境下的常态。把 primary 设成你主力模型fallback 放两个稳定通道empty_response_as_failure打开stream 在 fallback 上关掉这套组合能挡掉绝大多数空响应导致的 Agent 中断。最后留一个检查清单下次再遇到 Empty response按这个顺序过一遍原始响应打印了吗choices 是空数组吗stream 关掉试过吗fallback 列表读到了吗Key 和 Base URL 一致吗五个问题过完问题基本就定位了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询