DeepAI Paper Hermes Agent 教程,Hermes 模型与 Provider API 模型需要 max_completion_tokens,Hermes 却按网址猜参数:自定义 OpenAI 兼容端点的 400 坑

模型需要 max_completion_tokens,Hermes 却按网址猜参数:自定义 OpenAI 兼容端点的 400 坑

OpenAI-compatible API 最大的好处,是你可以把模型服务放在不同入口后面:自建网关、Azure OpenAI、OpenRouter、公司代理、或者 DeepAI API 中转站。

但兼容接口有一个常见陷阱:

参数规则跟模型有关,不一定跟域名有关。

NousResearch/hermes-agent issue #13901 就踩在这里。

Hermes 在选择 max_tokens 还是 max_completion_tokens 时,旧逻辑主要看 base URL host:

是不是 api.openai.com?

如果不是,就发送旧参数:

{"max_tokens": 4096}

问题是,很多自定义 OpenAI-compatible endpoint 后面跑的正是新模型族:

  • gpt-4o
  • gpt-4.1
  • gpt-5.x
  • o1
  • o3
  • o4

这些模型会拒绝 max_tokens,要求使用:

{"max_completion_tokens": 4096}

于是第一轮请求就炸:

{
  "error": {
    "code": "unsupported_parameter",
    "message": "Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.",
    "param": "max_tokens",
    "type": "invalid_request_error"
  }
}

这类错误很适合用一句话概括:

服务端按模型验参数,客户端却按域名猜参数。

复现场景:不是 api.openai.com,但模型是新模型族

issue 里的复现方式很典型:

export OPENAI_BASE_URL=https://my-openai-gateway.example.com/v1
export OPENAI_API_KEY=sk-...
export OPENAI_MODEL=gpt-5.4

然后启动:

hermes chat

随便发一条消息,请求就失败。

原因不是 API key 错,也不是 base URL 不能访问,而是 Hermes 发出了模型不接受的参数:

{"max_tokens": ...}

对新 OpenAI 模型族来说,应该发:

{"max_completion_tokens": ...}

根因:参数选择只看 URL host

issue 指向的核心函数是:

AIAgent._max_tokens_param

旧逻辑大致是:

def _max_tokens_param(self, value: int) -> dict:
    if self._is_direct_openai_url():
        return {"max_completion_tokens": value}
    return {"max_tokens": value}

也就是说:

  • 如果 base URL 是 api.openai.com:用 max_completion_tokens
  • 如果 base URL 是其他域名:用 max_tokens

这在旧模型时代看起来能工作。

但到了 OpenAI-compatible 网关时代,这个判断就不够了。

因为自定义域名后面完全可能是:

gpt-5.4
gpt-4o-mini
o3-mini
openai/gpt-4.1

模型族要求并不会因为你换了域名而改变。


同一个 bug 不只在主对话链路

评论里还补充了一个关键点:这个问题不只存在于主 agent path。

相同的 host-based assumption 也出现在 auxiliary path:

  • agent/auxiliary_client.py::auxiliary_max_tokens_param
  • agent/auxiliary_client.py::_build_call_kwargs

所以如果只修主对话路径,可能出现一种更隐蔽的状态:

聊天正常了,但标题生成、总结、后台任务、辅助模型调用仍然报 unsupported_parameter。

这也是 Agent 框架排错时最烦人的地方:主路径和辅助路径不一定共享同一套参数构造逻辑。


Azure OpenAI 也确认受影响

评论中有人确认,Azure OpenAI / Azure AI Foundry 自定义 endpoint 也能复现。

典型 endpoint 类似:

https://<resource>.openai.azure.com/openai/v1

受影响模型包括:

  • gpt-5.4-hermes
  • gpt-5.4-nano-hermes
  • gpt-4.1-hermes

Azure 返回的错误同样是:

Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead.

在 Telegram / gateway 模式下,这个错误会放大成更差的用户体验:

  • repeated retries;
  • fallback switching;
  • Primary model failed — switching to fallback
  • 任务被中断;
  • 用户以为模型不稳定。

但根因其实只是一个参数名选错。


为什么这个问题很容易误诊?

因为报错位置在请求参数,但用户看到的是“模型调用失败”。

很多人会先怀疑:

  • API key 无效;
  • base URL 写错;
  • 网关不兼容;
  • 模型不存在;
  • OpenRouter / Azure / 自建代理不稳定;
  • fallback 模型配置错;
  • Gateway 网络异常。

unsupported_parameter 这个错误非常明确。

只要看到:

Unsupported parameter: 'max_tokens'
Use 'max_completion_tokens' instead

就应该先查:

客户端是不是对新模型族仍然发送了 max_tokens?

更合理的判断方式:模型族 + URL,而不是只看 URL

issue 原始建议是增加一个共享 helper:

def model_forces_max_completion_tokens(model: str) -> bool:
    m = (model or "").strip().lower()
    if not m:
        return False
    if "/" in m:
        m = m.rsplit("/", 1)[-1]
    return (
        m.startswith("gpt-4o")
        or m.startswith("gpt-4.1")
        or m.startswith("gpt-5")
        or m.startswith("o1")
        or m.startswith("o3")
        or m.startswith("o4")
    )

然后在各个参数构造点里判断:

if self._is_direct_openai_url() or model_forces_max_completion_tokens(self.model):
    return {"max_completion_tokens": value}

这个思路的价值在于:

模型约束优先,域名只是辅助信号。

并且它兼容 OpenRouter 风格的模型名:

openai/gpt-4o-mini

通过去掉 / 前缀后,仍然能识别 gpt-4o-mini


官方后续:问题被部分绕开,但不是所有场景都消失

issue 后续关闭时提到,原始复现环境已经不再失败,原因是几个相关改动陆续合入:

  • gpt-5.x 现在无论 base URL 是什么,都会走 Responses API;
  • Azure OpenAI 加入 URL allowlist;
  • GitHub Copilot 加入 URL allowlist;
  • auxiliary calls 遇到 unsupported_parameter 400 会 reactive retry。

所以对 gpt-5.x、Azure、Copilot、辅助调用来说,实际影响被明显降低。

但评论也明确提到仍有窄口径缺口:

main agent path + custom non-Azure/non-Copilot endpoint + gpt-4o/4.1/o-series

也就是说,如果你用的是普通自定义 OpenAI-compatible endpoint,不是 Azure,不是 Copilot,并且后面挂的是 gpt-4o / gpt-4.1 / o-series,仍然要警惕这个参数选择问题。


对 DeepAI API 中转站用户的排查建议

DeepAI API 中转站提供 OpenAI-compatible API 入口,这类场景最容易遇到类似问题。

如果你在 Hermes 或其他 Agent 客户端里配置:

OPENAI_BASE_URL=https://your-openai-compatible-endpoint/v1
OPENAI_API_KEY=...
OPENAI_MODEL=gpt-4o-mini

然后看到:

unsupported_parameter: max_tokens

不要第一时间怀疑中转站不可用。

更应该检查:

1. 客户端是否把新模型族误判成旧模型; 2. 是否仍然发送 max_tokens; 3. 是否支持配置 max_completion_tokens; 4. 是否有 Responses API / Chat Completions API 的自动切换逻辑; 5. auxiliary model / title generation / summarization 是否也走同一套参数规则。

如果你使用 DeepAI 作为 OpenAI-compatible 接入层,重点是让客户端按模型能力构造请求,而不是按域名猜。


临时规避方案

如果暂时不能升级 Hermes,可以考虑这些方式。

1. 换成已被客户端识别的新模型路径

如果客户端对某些模型会自动走 Responses API,可以使用对应模型名,避免旧参数分支。

2. 在网关层做参数转换

如果你的代理可控,可以把请求中的:

{"max_tokens": 4096}

转换成:

{"max_completion_tokens": 4096}

但这要小心,不要对所有模型无脑转换,否则旧模型可能反向报错。

3. 使用客户端支持的 provider preset

Azure、Copilot 这类 provider 后续被加入 allowlist。

如果你的服务实际是 Azure OpenAI,就尽量走 Azure provider 配置,而不是泛化 custom OpenAI endpoint。

4. 升级到包含相关修复的 Hermes 版本

重点关注:

  • gpt-5.x Responses API routing;
  • Azure OpenAI URL allowlist;
  • Copilot URL allowlist;
  • auxiliary unsupported_parameter reactive retry。

FAQ

为什么 api.openai.com 正常,自定义 base URL 就报错?

因为旧逻辑把 api.openai.com 当作是否使用 max_completion_tokens 的主要判断依据。自定义 URL 即使后面是新模型,也会被发送 max_tokens

这是 DeepAI API 中转站的问题吗?

通常不是。unsupported_parameter: max_tokens 说明服务端正在按模型规则校验参数,客户端发错了参数名。DeepAI 作为 OpenAI-compatible 入口时,关键是客户端要使用正确参数。

为什么新模型不用 max_tokens

OpenAI 新模型族和 reasoning / Responses API 相关链路更倾向使用 max_completion_tokens,某些模型会直接拒绝 max_tokens

只改主 agent path 够吗?

不够。issue 中指出 auxiliary client 里也有类似逻辑,标题生成、总结、后台任务等辅助调用也可能触发同类错误。

最稳的修复思路是什么?

不要只按 URL 判断。应该结合模型名、provider 类型、API mode,以及服务端 400 错误做 fallback / retry。


总结

#13901 的教训很直接:

OpenAI-compatible endpoint 的参数选择,不能只看域名。

当自定义网关、Azure、OpenRouter、DeepAI API 中转站这类入口后面承载新模型族时,客户端必须按模型能力选择:

max_tokens

还是:

max_completion_tokens

否则用户看到的就不是“参数名错了”,而是整个 Agent 第一轮对话 400、反复重试、fallback 乱跳。

对 Agent 开发者来说,这不是一个小 if 判断,而是 provider abstraction 是否靠谱的分水岭。

Related Post

没装 Claude,Agent 却偏要调用 Claude:Hermes delegate_task 的“示例诱导”问题没装 Claude,Agent 却偏要调用 Claude:Hermes delegate_task 的“示例诱导”问题

Hermes delegate_task 在工具 schema 中把 claude 和 Claude Code 写成显眼示例,可能诱导 Agent 在未安装 Claude 的环境里尝试 Claude ACP。本文复盘 #22013:为什么工具说明本身也是 prompt surface,以及如何用环境检测和 schema gating 降低误调用。

Hermes agent deepai gpt5 custom endpoint api mode codex responses.png

Hermes Agent 接入 DeepAI API 中转站:gpt-5 自定义端点被强制走 codex_responses 怎么排查Hermes Agent 接入 DeepAI API 中转站:gpt-5 自定义端点被强制走 codex_responses 怎么排查

Hermes Agent 使用自定义 OpenAI-compatible 端点接入 API 中转站时,如果模型名以 gpt-5 开头,即使显式设置 api_mode=chat_completions,也可能被运行时强制切到 codex_responses,导致响应变慢或卡住。本文结合 Hermes Agent Issue #10473,整理 DeepAI API 中转站场景下的协议选择、模型命名和排查方法。

9000 轮配置,为什么跑着跑着又变成 90?Hermes Gateway 被旧 .env 反杀的配置优先级坑9000 轮配置,为什么跑着跑着又变成 90?Hermes Gateway 被旧 .env 反杀的配置优先级坑

Hermes Gateway 中 agent.max_turns 写成 9000,却在长任务心跳里显示 iteration X/90。本文复盘 #19158:为什么旧 .env 里的 HERMES_MAX_ITERATIONS=90 会通过 dotenv reload 覆盖 config.yaml,以及如何排查配置优先级。