“这个模型支持工具调用吗?”听起来像是在问模型,其实不是。它问的是模型以及你调用它所用的端点——同一个模型在一个端点上会回答“支持”,在另一个端点上却回答“不支持”。本页说明其中的原因,以及 Entity Enricher 如何通过实测而非采信目录来给出答案。
在经典的 /v1/chat/completions 端点上向 OpenAI 的 gpt-6 系列发送带函数工具的请求,该请求会被直接拒绝:
400 Bad Request Function tools with reasoning_effort are not supported. Use /v1/responses or set reasoning_effort to 'none'.
把同样的模型、同样的工具和同样的提示词发往 /v1/responses,它便能正常响应——两个结构化输出通道都可用,无论是否设置推理强度。模型本身没有任何变化,变的只是地址。
由此带来的影响
能力并非模型单独具备的属性,而是(模型,端点)这一组合的属性。因此,任何为每个模型只存一个真/假值的目录,回答的都是它其实看不到的问题。
路由就是请求被发送到的那个端点。相同的提供方、相同的模型、相同的 API 密钥——但传输格式不同、请求结构不同,对哪些内容可以组合使用的规则也不同。OpenAI 提供了两种路由,业内大多数厂商照搬了较早的那一种。
| 路由 | 交互的形态 | 谁支持它 |
|---|---|---|
Chat Completions /v1/chat/completions2023 | 无状态:你发送完整的消息列表,得到下一条消息。两次调用之间服务端不保留任何内容。 | OpenAI,以及几乎被其他所有厂商效仿的事实行业标准——xAI、DeepSeek、阿里巴巴、Groq、Together、Ollama 以及大多数 OpenAI 兼容网关。 |
Responses API /v1/responses2025 | 返回带类型的条目列表——推理条目、消息、函数工具调用、托管工具调用——在服务端保留状态,并可将推理内容从一轮传递到下一轮。 | 仅限 OpenAI。一个形似的端点即便使用 Chat Completions 的格式,也不会自动提供该端点。 |
这正是“OpenAI API”在今天含义模糊的原因:它有两个,彼此重叠,且执行的规则并不相同。
推理模型打破了消息模型。模型的内部推理并不是一条消息:它既不是模型对你说的话,也不是你对模型说的话。Chat Completions 只有一个消息列表,因此无处安放推理内容——而它无法表示的东西,就会在轮次之间被丢弃。
Responses API 正是围绕这一缺口设计的。推理在返回的列表中是一等条目;它可以加密返回并在下一轮回放,因此模型是从自己的思考继续,而不必重新构建它。
函数工具调用是一次往返:模型请求调用工具,你的代码给出结果,模型继续生成。在 Chat Completions 上没有地方能在这次往返之间保留推理过程,因此继续生成时会悄悄丢掉它——模型只能把你已经付过费的思考重做一遍,而且很可能得出不一样的结果。
因此提供商会拒绝这种组合,而不是悄悄降级。这条错误信息并不是模型的限制;而是较旧的 API 承认自己没有位置来容纳该模型产出的内容。
如果所有能力都偏好同一个端点,路由不过是无人需要关心的实现细节。但事实并非如此。
通过工具通道实现结构化输出,正是 Chat Completions 在推理模型上拒绝的组合;而提供商托管的网络搜索工具也只有在那里才被接受。
Responses API 这条路径无法承载原始音频字节附件,因此支持音频的模型只能通过较早的那个端点访问。
同一个模型、两种能力、两个不同的端点。没有哪一个 true/false 能同时如实描述两者——正因如此,Entity Enricher 会按路由记录能力图谱;也因此,当一次运行所需的两种能力分处两条不同路由时,系统会明确指名拒绝,而不是悄悄舍弃其中之一。
能力探测不会发送人工构造的合成请求。它会强制启用待测能力,驱动真实的代理路径——与一次增强运行所走的代码完全相同——并由提供商的响应来判定结果。因此,被测量的是应用实际能做到什么,而不是 API 理论上能接受什么。
整条能力阶梯——结构化输出通道、视觉、PDF、音频、联网搜索、推理及其按请求的努力程度控制——会针对路由器可选择的每条路由各运行一次。对几乎所有提供商而言那都是单一路由,探测成本与以往完全相同;只有确实可通过两种方式访问的 OpenAI 形态提供商才会被测量两次。
若某条路由连第一次纯文本调用都不响应,就直接不做测量;此后,已探测过的模型只会被路由到有响应的那些路由上——无论是在选择路由时,还是在某次运行失败时,都不会回退到别的端点。没有任何路由响应的模型会被停用;两种结构化输出通道都不具备的模型同样会被停用:纯文本永远不作为回退手段,这样的模型连一次富化都无法完成。只有后续探测证明它具备某种通道,才会让它恢复启用。能力绝不会因为某次运行失败而被改动——只有探测才会去测量它们。
请求被接受,且模型给出了预期类型的回答。
记录为该路由上实测为 true——其优先级高于任何抓取而来的声明。
提供商明确拒绝了该能力,或客户端根本拒绝构建该请求。
唯一允许写入 false 的结果。拒绝同样是免费的:绝不计费。
其他任何情况:速率限制、缺少访问层级、超时、提供方 5xx、无法识别的错误。
不会写入任何结果,留待下一次探测来定论。
这两种错误的代价并不相同。错判为未知只需付出一次重新探测的代价:该能力稍后会被再次测量。而错判为不支持则是静默且永久的——它会在所有地方降级该模型,把它从选择器和路由决策中移除,而且没人会知道原因。因此,无法识别的失败什么也证明不了,也就按“什么也没证明”记录。
HTTP 200 只能证明请求被接受,并不能证明模型真的读取了附件——提供方完全可以愉快地接收一张图片,再把它交给一个从不查看它的模型。因此,每个携带载荷的探测,所用载荷的答案都是可以校验的:
| 能力 | 发送的载荷 | 提出的问题 |
|---|---|---|
| 视觉 | 一张纯红色图片 | “其中占主导的是什么颜色?” |
| PDF 输入 | 只包含一个词的 PDF | “文档中出现的是哪个词?” |
| 音频输入 | 一个包含若干可计数音调的音频文件 | “有多少声独立的提示音?” |
两个独立的结果,有意如此
内容答案会记录在判定旁边,绝不会改变能力标志。一个把红色方块认错的小模型依然支持视觉——传输成功,模态得到承载,只是模型能力较弱而已。把“做不到”与“做得差”混为一谈,会因为一个错词就悄悄抹掉一项本可用的能力;将两者分开记录,则能同时告诉你这两件事。
LiteLLM 或 PricePerToken 等公开模型目录把能力当作模型的单一事实来陈述,不附带任何端点信息。这并非疏忽——它们根本没有这样的字段。但这意味着一条声明在总体上完全正确,却仍可能对你的运行所使用的端点不成立。
“该模型支持工具调用。”在某处为真,却没说是在哪里。对于尚未被探测过的模型,这可以作为一个起点。
“在这个端点上,该模型此刻接受了工具调用。”它标明了自己的路由,因此可以与某次运行实际采用的路由作比较。
记录路由名称也是让测量结果长期保持可信的关键。在模型已不再使用的端点上得出的判定,并不比一条声明更有说服力——它所证明的是另一回事。若不同时记录路由,旧的测量结果将与当前结果无从区分,并会在错误的端点上压过目录中的信息。一旦记录了路由,它便自然不再适用;在正确端点上的测量出现之前,该声明依旧有效。
应用内的模型目录和公开的模型目录都在筛选器旁提供了路由选择器。它决定每一行的能力图标是针对哪条路由给出答案的。
每一行反映的都是该模型实际所服务的端点。此视图与你运行增强时得到的结果一致;除非你要调查某个特定端点,否则应以该视图为准。
将整张表固定到某个端点后,图标就只反映该端点的情况——这样你在端点之间切换时就能看到某项能力的出现或消失,也能在同等条件下比较两个模型。
只有一条可达路由的模型根本不会显示选择器:无可选项,其图标已经针对它唯一的端点给出了答案。只有当歧义真实存在时,选择器才会出现。
多数情况下什么都不用做——这正是设计的用意。你从不需要挑选路由,只需选择模型并描述你的运行,由路由器挑选能够承载该运行所需能力的端点:工具和网络搜索会被发往接受它们的端点,音频附件会被发往支持音频的端点。
你唯一会察觉到的情况是真正的冲突——例如对同一个 OpenAI 模型既要求联网搜索又附加音频文件,因为没有任何单个端点能同时支持两者。这类运行会被明确拒绝,并指出冲突的两项能力以及每条路由各自能承载什么,而不是照常运行却静默丢弃其中之一。去掉其中一项要求,或改用同时支持两者的模型,即可继续。
当某个能力图标让你意外时,“路由”选择器就是答案所在:你看到的几乎总是这样一个模型——它的答案取决于问题是在哪里提出的。