---
title: "Provider 路由"
description: "同一个模型有多家 provider 时，pirouter 为每个请求选一家。本页说明默认规则、工作区设置、按请求覆盖，以及筛空之后怎么排查。"
canonical: "https://pirouter.ai/zh/docs/routing"
last-updated: "2026-10-13"
lang: "zh"
source: "content/docs/zh/(start)/routing.mdx"
---

# Provider 路由

同一个模型有多家 provider 时，pirouter 为每个请求选一家。本页说明默认规则、工作区设置、按请求覆盖，以及筛空之后怎么排查。

同一个模型常常由多家 provider 提供。pirouter 为每个请求从中选一家，失败时自动换下一家。你可以在工作区里设默认规则，也可以在单个请求里覆盖。

## 默认怎么选

没有任何设置时，pirouter 在能服务这个模型的 provider 之间按权重分配流量。价格不参与默认排序。

被标记为不可用的 provider 不会被选中。

一个模型有哪些 provider，看模型页，或者调 `GET /v1/models/{vendor}/{model}/endpoints`，返回里每一条的 `provider` 就是下面各处要写的名字。

## 在工作区里设默认规则

打开控制台的 **路由** 页。只有工作区所有者能修改，其他成员可以查看。

- [打开控制台](https://app.pirouter.ai): app.pirouter.ai

| 设置       | 作用                                                                    |
| -------- | --------------------------------------------------------------------- |
| **排序**   | 同一模型有多家 provider 时先选哪一家：**均衡**、**价格优先** 或 **自定义顺序**                   |
| **仅使用**  | 只把请求发给这些 provider                                                     |
| **不使用**  | 永远不把请求发给这些 provider                                                   |
| **备用模型** | 请求的模型不可用时，按顺序改用这些模型，见[模型回退](https://pirouter.ai/zh/docs/fallbacks.md) |

保存后，下一个请求就按新规则走。

## 按请求覆盖

在请求体的顶层加一个 `provider` 对象。pirouter 在 OpenAI、Anthropic、Gemini 三种协议的请求里都接受 `provider` 对象。

```bash
curl https://api.pirouter.ai/v1/chat/completions \
  -H "Authorization: Bearer $PIROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-haiku-5.5",
    "messages": [{ "role": "user", "content": "Hello!" }],
    "provider": { "only": ["azure"] }
  }'
```

你应该看到状态码 `200`。这个请求只会发给 `azure`。

| 字段                | 类型                   | 作用                                                    |
| ----------------- | -------------------- | ----------------------------------------------------- |
| `order`           | 字符串数组                | 按这个顺序优先尝试。没列出的 provider 排在后面，仍然会被尝试                   |
| `allow_fallbacks` | 布尔，默认 `true`         | 设为 `false` 并且写了 `order` 时，只有 `order` 里的 provider 可以服务 |
| `sort`            | `"price"`            | 按输入价加输出价从低到高排                                         |
| `only`            | 字符串数组                | 只用这些 provider                                         |
| `ignore`          | 字符串数组                | 不用这些 provider。和 `only` 冲突时以 `ignore` 为准               |
| `data_collection` | `"allow"` 或 `"deny"` | `"deny"` 排除会用你的数据训练、或没有声明是否训练的 provider               |
| `zdr`             | 布尔                   | `true` 只保留零数据留存的 provider                             |
| `quantizations`   | 字符串数组                | 只用这些量化精度的部署                                           |

### provider 的名字

名字不区分大小写。除了目录里的名字，还接受三个别名：`google-vertex`（即 `gcp`）、`google-ai-studio`（即 `gemini`）、`amazon-bedrock`（即 `aws`）。

不认识的名字返回 `422`，错误信息指出是哪个字段的第几项。在名字后面加 `/` 和区域的写法目前不支持，同样返回 `422`。

空数组和 `null` 都当作没有设置。`"only": []` 不表示一家都不许用。

### 顺序与价格

`order` 是偏好，不是白名单。要把请求限制在列出的几家，加上 `"allow_fallbacks": false`：

```json
{ "provider": { "order": ["anthropic", "azure"], "allow_fallbacks": false } }
```

`"sort": "price"` 比较的是输入价加输出价，取这个请求实际会被计费的那一档。和 `order` 一起用时，价格只在 `order` 的同一档内排序。`"throughput"` 和 `"latency"` 两种排序目前不支持，返回 `422`。

### 数据策略与量化

`"data_collection": "deny"` 排除两类 provider：会用提示词训练或没有声明的，以及数据留存既不是零留存也不是有限留存的。`"zdr": true` 更严，只保留零留存的。

`quantizations` 可以写 `fp4`、`int4`、`int8`、`fp8`、`fp16`、`bf16`、`fp32`、`unknown`。设了之后，没有声明精度的部署也会被排除。

## 两层设置怎么合并

工作区设置和请求里的 `provider` 同时存在时：

- **限制只能收紧。** `only` 和 `quantizations` 取两层的交集，`ignore` 取并集。工作区设了 `data_collection` 为 `deny`、只用零留存，或关闭了回退，请求不能放宽。
- **排序整体替换。** 请求只要写了 `order` 或 `sort` 中的一个，工作区的排序方式就整个不生效。例如工作区设了自定义顺序，请求写 `"sort": "price"`，这个请求只按价格排。

## 一家都不剩时

设置把所有 provider 都排除之后，请求返回 `404` 和错误码 `no_eligible_provider`。请求没有被转发，不计费。

```json
{
  "error": {
    "message": "No allowed provider can serve this model: the request's provider preferences, together with the workspace routing settings, exclude every provider that serves it. Providers serving it: anthropic, azure.",
    "type": "invalid_request_error",
    "code": "no_eligible_provider",
    "metadata": {
      "available_providers": ["anthropic", "azure"],
      "routing_funnel": [
        { "step": "initial", "providers": 2 },
        { "step": "data_policy", "providers": 2 },
        { "step": "quantization", "providers": 2 },
        { "step": "provider_ignore", "providers": 2 },
        { "step": "provider_only", "providers": 0 }
      ],
      "failed_step": "provider_only"
    }
  }
}
```

`metadata` 告诉你是哪一步排空的：

- `available_providers`：任何设置生效之前，服务这个模型的 provider。
- `routing_funnel`：每一步之后还剩几家。
- `failed_step`：把候选排空的那一步。

| `failed_step`       | 对应的设置                       |
| ------------------- | --------------------------- |
| `data_policy`       | `data_collection`、`zdr`     |
| `quantization`      | `quantizations`             |
| `provider_ignore`   | `ignore`，加上工作区的 **不使用**     |
| `provider_only`     | `only`，加上工作区的 **仅使用**       |
| `fallback_disabled` | `allow_fallbacks` 为 `false` |

这个错误和 `404 model_not_found` 不同：`model_not_found` 是模型名写错了，`no_eligible_provider` 是模型存在、但设置把 provider 排空了。

## 失败时自动换一家

选中的 provider 在返回第一个字节之前失败时，pirouter 换下一家重试，你不需要自己重试。

- **会换**：上游返回 `429`、`500`、`502`、`503`，连接失败，或者上游拒绝了平台自己的凭证（`401`、`403`，这不是你的 key 的问题）。
- **不会换**：其他 `4xx`（例如请求参数错误、上下文超长）、超时，以及响应已经开始之后出的错。
- 一个请求最多尝试 5 次，流式和非流式相同。
- 全部失败时，返回最后一次上游的状态码和响应体。

`"allow_fallbacks": false` 而没有写 `order` 时，重试只在第一次选中的那家 provider 内进行。

## 请求实际走了哪家

看响应头。由 provider 实际应答的响应带下面这些响应头，给 curl 加 `-i` 可以看到：

| 响应头                        | 内容                                   |
| -------------------------- | ------------------------------------ |
| `X-Pi-Provider`            | 实际处理这个请求的 provider                   |
| `X-Pi-Model`               | 实际处理这个请求的模型 ID，和响应体的 `model` 相同      |
| `X-Pi-Upstream-Model`      | 上游自己报告的模型名                           |
| `X-Pi-Upstream-Request-Id` | 上游自己的请求 ID，找 provider 排查时用；上游没有提供时不带 |
| `X-Pi-Trace-Id`            | 平台的请求 ID，每个响应都有，报障时带上它               |

网关自己给出的错误（鉴权、余额、限流、参数校验、没有可用 provider）没有 provider 处理过，只带 `X-Pi-Trace-Id`。

失败后换过 provider 时，这些响应头说的是最后给出回答的那一次。在控制台的 **日志** 里也能按 `X-Pi-Trace-Id` 找到这次请求。

## 写错了会怎样

`provider` 里的错误一律返回 `422` 和错误码 `validation_failed`，`message` 以 `Invalid provider preferences:` 开头，后面指出字段：

| 情况            | `message` 里的说明                              |
| ------------- | ------------------------------------------- |
| 不认识的键         | `provider: unrecognized key "foo"`          |
| 认识但还没实现的键     | `provider.max_price is not supported yet`   |
| 不认识的 provider | `provider.only[0]: unknown provider "nope"` |
| 类型不对          | `provider.zdr: must be a boolean`           |

## 目前没有的能力

- **用 API 读写工作区的路由设置。** 路由设置只能在控制台里改；API 能做的是在每个请求里带 `provider`。
- **按吞吐或延迟排序。** `sort` 只接受 `"price"`。
- **价格上限等其余字段。** `max_price`、`require_parameters`、`preferred_min_throughput`、`preferred_max_latency`、`enforce_distillable_text`、`options` 会返回 `422`。

## 下一步

- [模型回退](https://pirouter.ai/zh/docs/fallbacks.md): 请求的模型不可用时，自动改用另一个模型。
- [API 参考](https://pirouter.ai/zh/docs/api/chat-completions.md#body): `provider` 对象在请求体里的完整定义。
