名叫 openai.azure.com 的网关却用 Anthropic 协议时,你让 Agent 怎么办?
本文最后更新于 2026年4月29日 晚上
根因:这个网关虽然主机名是
openai.azure.com,但同一台服务同时暴露了 OpenAI-compatible 与 Anthropic Messages 两套协议。GPT 要走/openai/v1/...,Claude 要走/anthropic/v1/...。
再加上 Goose 新版更新收紧了对 openai 的参数验证,所有看起来就是上一个版本还能用,升级了就突然挂了。
总结论
这不是一个纯 OpenAI 网关。
虽然主机名是...openai.azure.com,但它同时提供:/openai/v1/models/openai/v1/chat/completions/anthropic/v1/messages?api-version=
GPT-5.5 和 Claude 走的不是同一条协议面。
- GPT-5.5 应该走
/openai/v1/chat/completions,并且要用max_completion_tokens - Claude 应该走
/anthropic/v1/messages,并且要用max_tokens
- GPT-5.5 应该走
Hermes 早期现象:
- 能看到 Claude、能用 GPT-5.5
- 但 Claude 不能请求
这不是因为 Claude 不存在,而是因为 Hermes 当时主要按 endpoint 走 OpenAI-compatible 路线;/openai/v1/models里能看到 Claude,不代表 Claude 也支持/openai/v1/chat/completions。
Goose 早期现象:
- 看不到 GPT-5.5、Claude 4.7
- Claude 也不能正常配置
后来可以拆成两个原因: - 看不到部分新模型,是因为 Goose 在抓取模型列表后又做了一层 canonical filtering
- Claude 不能用,是因为 Goose 后来对 provider / 协议边界收紧了,Claude 不该继续挂在
Azure OpenAIprovider 下,而应该单独建一个 Anthropic-style provider
Goose 的正确修法:
不再试图让 Claude 继续走azure_openai,而是单独创建一个 Anthropic-style provider,host 指向:1
https://...openai.azure.com/anthropic/v1这个地址看起来很抽象,但它确实就是当前网关上 Claude 的正确入口。
现象
1. Hermes 这边
当时观察到的是:
- Hermes 能看到 Claude
- Hermes 能用 GPT-5.5
- 但 Hermes 不能真正请求 Claude
后来的直接探测把这件事解释清楚了:
/openai/v1/models会把 Claude 列出来- 但 Claude 并不支持
/openai/v1/chat/completions - Claude 真正能工作的,是
/anthropic/v1/messages?api-version=
所以 Hermes 当时的问题,本质上不是“看不到 Claude”,而是:
看到了 Claude,但按 OpenAI-compatible 方式去请求它。
2. Goose 这边
当时观察到的是:
- Goose 看不到 GPT-5.5
- Goose 看不到 Claude 4.7
- Goose 也不能正常用 Claude
这三件事后来也都能解释:
2.1 为什么 Goose 看不到 GPT-5.5 / Claude 4.7
不是网关没返回,而是 Goose 自己又过滤了一遍。
也就是说:
- 上游模型列表不一定为空
- 但 Goose 在展示前还会做一层 canonical mapping / filtering
- 因此一些新模型虽然在 live
/models里存在,Goose 不一定会直接展示
2.2 为什么 Goose 不能用 Claude
不是因为 Claude 本身坏了,而是 Goose 后来的 provider 边界更严格了。
Claude 在这个网关里本来就不该继续挂在 Azure OpenAI provider 下面,而应该作为 Anthropic-style provider 单独配置。
所以现在最自然的方式其实是:
- GPT 继续走 Azure OpenAI / OpenAI-compatible 路线
- Claude 单独建一个 Anthropic-style provider
- host 直接填:
1 | |
虽然这个 host 看上去很怪,因为域名还是 openai.azure.com,但 path 这一层实际上讲的是 Anthropic 协议,这正是它能工作的原因。
直接验证结果
这次问题最后是靠直接 probe 定下来的,不是靠 UI 猜出来的。
1. 模型列表
1 | |
这说明:
- Claude 确实存在于这个网关的可见模型列表里
- GPT-5.5 也存在
2. GPT-5.5
1 | |
这说明 GPT-5.5 的正确组合是:
- endpoint:
/openai/v1/chat/completions - max 字段:
max_completion_tokens
3. Claude
1 | |
这一步是整个问题的关键。
它说明:
- Claude 虽然在
/openai/v1/models里可见 - 但 Claude 并不属于
/openai/v1/chat/completions那一侧 - Claude 真正属于
/anthropic/v1/messages这条协议面
代码出处
下面只记最相关的代码点。
1. Goose
1.1 Azure provider 仍然是 OpenAI-compatible 路线
文件:
crates/goose/src/providers/azure.rscrates/goose/src/providers/openai_compatible.rs
关键点:
azure.rs里会把 Azure provider 包成 OpenAI-compatible provider- 请求前缀包含:
1 | |
也就是说它本质上还是沿着 OpenAI-compatible 这一族在走。
1.2 Goose 会拉模型,但之后还会再过滤
文件:
crates/goose/src/providers/openai_compatible.rscrates/goose/src/providers/base.rscrates/goose-cli/src/commands/configure.rs
关键点:
openai_compatible.rs里有fetch_supported_models()base.rs里有fetch_recommended_models()- configure 流程里实际拿的是
fetch_recommended_models()的结果
所以 Goose 展示出来的模型,不只是 live list,还经过了一轮自己的推荐过滤。
1.3 Anthropic provider 支持单独走 Anthropic 协议
文件:
crates/goose/src/providers/anthropic.rs
关键点:
- 这个 provider 自己就是 Anthropic 协议族
- 它使用独立的 base URL / host
- 所以把 host 指到:
1 | |
虽然域名还是 Azure 风格,但只要该 path 实现的是 Anthropic Messages,就能工作
2. Hermes
文件:
hermes_cli/models.pyrun_agent.pyhermes_cli/runtime_provider.pyagent/transports/chat_completions.py
关键点:
- Hermes 会探测
/models,所以能先看到 Claude - 但 runtime 如果按 OpenAI-compatible /
chat_completions去走,就会把 Claude 送错协议面 - Hermes 代码里也明确区分了不同
api_mode,例如:chat_completionsanthropic_messages
所以 Hermes 这边后来真正的经验也一样:
不要只看 endpoint 主机名,要看它最终走的是哪种
api_mode。