> ## Documentation Index
> Fetch the complete documentation index at: https://gomodel.enterpilot.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 虚拟模型

> 使用 GoModel 虚拟模型对外提供稳定的模型名称，并在不修改应用代码的情况下测试模型替换。

![GoModel 控制台的模型页面，显示 New virtual model 按钮和一条已启用的重定向记录](https://mintcdn.com/gomodel/tq6QDIYBi0w3y99d/features/virtual-models.png?fit=max\&auto=format\&n=tq6QDIYBi0w3y99d\&q=85\&s=6558da4a6f35e44e7597001f3b09b405)

## 虚拟模型的作用

虚拟模型是由您定义的模型，而不是提供商公布的模型。GoModel 根据您是否为其指定目标来确定其类型：

* **重定向**（别名）是一个指向具体提供商模型的稳定名称。您的应用在 `model` 字段中发送该名称，
  GoModel 会在将请求发送到上游之前对其进行解析。
* **访问策略**没有目标。它按 `user_path` 控制对现有模型选择器的访问，或将其完全禁用，而不会新增名称。
  有关访问控制，请参阅[用户路径](https://gomodel.enterpilot.io/docs/zh/features/user-path.md#模型访问)。

两者都可以在管理控制台的 `Models -> New virtual model` 中创建和管理。填写 **Target model**
即创建重定向；留空则在 **Source** 选择器上创建访问策略。

无论是从控制台还是通过 Admin API 保存，GoModel 都不会持久化空的、无效的访问策略。如果一条已保存的
策略没有目标、用户路径、描述或减速设置，且其启用状态与继承的或默认的访问状态一致，GoModel 会删除
所存储的记录。如果某条原本为空的策略仍然覆盖了被禁用的默认值或继承的用户路径限制，GoModel 会保留它。

## 添加人为的模型延迟

在具体模型或重定向上设置 **Slowdown**，即可按实测推理时间的一定比例增加延迟。有效值范围为 `0.1` 到 `10`；
例如，`0.5` 会增加 50%，因此上游耗时 2 秒的响应会在大约 3 秒后返回。设置为 `0` 可显式禁用减速，
包括从具体模型继承的减速。将该字段留空则表示不指定该设置。延迟会响应请求取消。

对于流式响应（Chat Completions 和 Responses API 的 SSE，以及[音频端点](https://gomodel.enterpilot.io/docs/zh/advanced/audio-api.md#流式传输)），
GoModel 会在后台读取上游数据，并按缩放后的时间线释放读取到的每个数据块。这样可以保留提供商数据块之间的
相对时间间隔，但被延迟的数据块会在内存中累积。因此，在较长或高吞吐的流上，或在较大的音频响应上使用较大的
系数，可能会占用大量内存；在生产环境中请不要设置减速，它在那里没有任何用途。实时 WebSocket 会话不会被减速。

当别名及其具体目标都设置了减速时，以别名的值为准。没有自己的值的别名会继承所选目标模型的减速；
将别名设置为 `0` 可覆盖这种继承。这同样适用于选定目标之后的负载均衡虚拟模型。

本页其余部分介绍重定向。

## 使用稳定的名称

您可以对外提供 `regular` 和 `smarter` 这样的名称，而不是特定于提供商的模型 ID。

* `regular` -> `anthropic/claude-sonnet-4-6`
* `smarter` -> `anthropic/claude-opus-4-6`

然后您的应用可以发送：

```json theme={null}
{
  "model": "regular",
  "messages": []
}
```

## 在多个模型之间进行负载均衡

一个重定向可以指向**多个**目标模型，并在它们之间分配请求。在编辑器中添加多个目标（**Add target**），
然后选择一种**策略**：

* **Round-robin**（轮询，默认）在各目标之间均匀轮换请求。为每个目标设置 **weight**（权重）可以调整分配比例：
  权重为 `2` 的目标接收的流量是权重为 `1` 的目标的两倍。
* **Lowest cost**（最低成本）将每个请求路由到当前可用的最便宜目标，按模型注册表中输入 + 输出的每 Token
  价格排序。只要存在有定价的目标，没有注册表定价的目标就会被跳过；如果所有目标都没有定价，则使用列表中的第一个目标。
* **Failover**（故障转移）始终路由到按列表顺序排列的第一个当前可用目标：这些目标构成一个优先级列表，
  只有当排在前面的所有条目都不可用或失败时，后面的条目才会提供服务。权重和会话保持不适用。
* **Adaptive**（自适应，GoModel Pro）选择观测到的延迟、错误率和价格最优的目标，避开受速率限制或正在失败的目标，
  并将会话保持在提示词缓存已预热的提供商上。请参阅[智能路由](https://gomodel.enterpilot.io/docs/zh/pro/intelligent-routing.md)。在开源版本中，
  该策略会回退为轮询。
* **Plugin**（插件，`strategy: plugin`）将选择委托给一个已加载的、由 `strategy_plugin` 指定名称的
  [路由策略插件](https://gomodel.enterpilot.io/docs/zh/advanced/plugins.md#路由策略插件)，并通过 `strategy_config` 为每个重定向单独配置。
  内置的 `cheapest_healthy` 优先选择近期错误率可接受的最便宜（或最快）目标。权重不适用；如果插件失败或缺失，
  重定向会回退为加权轮询。

网关当前无法提供服务的目标（未知模型、提供商宕机）会被自动跳过，因此只要还有一个目标可用，重定向就能继续工作。
您已**禁用**的目标模型，或请求的 API 密钥或 `user_path` 不允许使用的目标模型也是如此：所有策略都会绕开它，
因此禁用某个模型（例如在其提供商的配额耗尽期间）会将其流量转移到其他目标，直到您重新启用它。当所有可用目标
都被禁用或拒绝时，请求会以 `model_access_denied` 失败；当完全没有可用目标时（未知模型、提供商宕机），
该名称会像其他模型名称一样被解析，通常以模型未找到错误告终。直接指定已禁用模型的请求仍会失败。
只有一个目标的重定向，其行为与普通别名完全相同。

无论采用哪种策略，其余目标都构成该重定向的**故障转移链**：当所选目标以可重试的错误失败时，请求会按列表顺序
在其他可用目标上重试。编辑器中的 **Failover** 复选框（默认勾选；在代码中为 `failover: false`）可以为单个重定向
关闭此行为，这样失败的请求会直接返回错误；也可以全局关闭，或通过请求的工作流关闭。关于触发重试的错误，
以及如何通过覆盖真实模型为其添加故障转移链，请参阅[故障转移](https://gomodel.enterpilot.io/docs/zh/features/failover.md)。

属于同一个被检测到的客户端会话的请求，会固定在首次为该会话提供服务的目标上（**Session keeping**，默认开启），
因此对话在多轮之间都能保持提供商的提示词缓存处于预热状态。取消勾选编辑器中的 **Session keeping** 复选框
（或设置 `session_affinity: false`）即可恢复无状态的负载均衡，请参阅[会话保持](https://gomodel.enterpilot.io/docs/zh/features/session-keeping.md)。

```json theme={null}
{
  "model": "smart",
  "messages": []
}
```

例如，可以使用轮询策略将 `smart` 同时指向 `openai/gpt-4o` 和 `anthropic/claude-sonnet-4-6` 来分担负载，
或者使用成本策略将其指向一个高端模型和一个经济型模型，从而始终选择更便宜的那个。

## 调整目标顺序

目标列表是一个有序列表，并且可以在编辑器中编辑：将每个目标行左侧的**拖动手柄**（☰）拖到另一行上，即可改变其位置。
放置处的行不会被替换：被拖动的行会移动到该位置，中间的行会依次移位。键盘用户可以聚焦手柄，然后按
**ArrowUp** / **ArrowDown** 完成同样的移动。

只有当重定向有多个目标时才会显示手柄，所有策略都会受益于此：对于 **failover**，顺序就是优先级链
（位置 `1` 最先尝试）；对于负载均衡策略，位置 `1` 是队列中的第一个位置。目标移动时，其权重和显式指定的提供商
会随之一起移动。

## 链式虚拟模型

重定向目标可以指定**另一个虚拟模型**，而不是具体模型。内部虚拟模型在请求时解析，因此其目标、权重和策略只需定义一次，
即可在所有引用它的地方复用，不会出现逐渐偏离的重复目标列表。

```yaml theme={null}
virtual_models:
  # 共享基础：在小模型之间轮询。
  - source: cheap
    targets:
      - { model: openai/gpt-4o-mini }
      - { model: groq/llama-3.3-70b }

  # 叠加在其上：cheap 池作为与高端模型并列的一个分支。
  - source: smart-frugal
    strategy: round_robin
    targets:
      - { model: cheap, weight: 3 }
      - { model: openai/gpt-4o }

  # 指向该池的普通别名。
  - source: production
    target: cheap
```

解析是逐层组合的：外层策略选择其中一个分支（链式分支与其他目标一样计数，并遵循其权重），而被选中的虚拟模型分支
再在自己的目标中应用自己的策略。会话保持在每一层都会生效，因此一个会话始终停留在同一个具体模型上。

* **cost** 策略按链式分支背后当前可用的最便宜具体模型为其计价。
* 被**禁用**或没有可用目标的链式虚拟模型，会像不可用的提供商模型一样被跳过。
* 只有**外层**虚拟模型的 `user_paths` 和 `slowdown` 会作用于请求；通过链访问内层虚拟模型时，不会参考其作用域。
* `/v1/models` 会使用链式重定向第一个可用具体模型的元数据来列出该重定向。

以单个名称书写的目标，无论在何处声明（`config.yaml`、控制台或 Admin API），都按名称匹配，因此即使
`team/cheap` 看起来像 `provider/model`，它也会指向同名的虚拟模型。显式设置 `provider`
（`{ provider: groq, model: llama-3.3-70b }`）可以将目标固定到具体模型，而不受任何同名虚拟模型的影响。

如果一个[覆盖真实模型](#覆盖模型)的重定向将该模型列在自己的目标之中（当您在 Models 页面为模型添加回退或
负载均衡时，所创建的就是这种结构），它是为该模型提供保护，而不是替换它。指定该模型的别名或任何其他重定向都会链接到
这层保护上，从而获得与直接请求相同的负载均衡和故障转移，但有一个例外：一个此类保护对另一个受保护模型的回退引用，
指向的是该模型本身，而不是另一个保护的回退列表。正是这一点让两个模型可以互相保护而不会形成循环，
也避免了回退模型的回退被一并卷入。由于对受保护模型的直接引用指向的是模型而不是保护层，因此即使存在此类引用，
也可以删除该保护层，这些引用会直接恢复为指向具体模型。*没有*将其源模型列入目标的覆盖则会替换该模型，
并像其他虚拟模型一样被链式解析：引用会链接到它的目标，并且在这些引用被重新指向之前，它无法被删除或重命名。

链不能有环（`a -> b -> a` 会被拒绝，并列出该循环），且深度最多为 8 个虚拟模型。从控制台保存时以及启动时加载
声明式条目时，都会执行这两条规则。保存时目标必须已存在，因此请先创建内层虚拟模型；被其他虚拟模型链接引用的虚拟模型，
在这些引用被重新指向之前无法被删除或重命名。

## 重命名或重新指向重定向

在控制台中打开一个重定向，编辑其 **Source** 即可重命名。GoModel 会一步将定义迁移到新名称，旧名称将不再能被解析，
因此请更新仍在发送旧名称的应用。重命名为已属于另一个虚拟模型的名称会被拒绝；重命名为具体模型名称则会覆盖该模型
（参见[覆盖模型](#覆盖模型)）。

覆盖模型的重定向也可以用同样的方式编辑，请使用模型行上的 **Edit redirect** 操作。以代码声明的虚拟模型，
其 `source` 在配置中是固定的；请通过编辑声明来重命名它们。

## 以代码定义虚拟模型

以基础设施即代码方式管理配置的运维人员，可以在 `config.yaml` 或 `VIRTUAL_MODELS` 环境变量中声明虚拟模型，
而不是使用控制台。声明式条目在启动时应用，会**覆盖**管理存储中具有相同 `source` 的任何记录，并在控制台中以只读方式显示
（带有 **Config** 徽标）。要更改它们，请编辑配置。

在 `config.yaml` 中：

```yaml theme={null}
virtual_models:
  # 普通别名。
  - source: regular
    target: anthropic/claude-sonnet-4-6
    slowdown: 0.5

  # 加权轮询负载均衡器。
  - source: smart
    strategy: round_robin
    targets:
      - model: openai/gpt-4o
        weight: 2
      - model: anthropic/claude-sonnet-4-6

  # 基于成本的负载均衡器。
  - source: cheap
    strategy: cost
    targets:
      - { model: openai/gpt-4o }
      - { model: groq/llama-3.3-70b }
```

同样的列表也可以写成单个环境变量（JSON），它会覆盖 `config.yaml` 中 `source` 相同的条目：

```env theme={null}
VIRTUAL_MODELS=[{"source":"smart","strategy":"cost","targets":[{"model":"openai/gpt-4o"},{"model":"groq/llama-3.3-70b"}]}]
```

每个条目接受 `source`、单个 `target`（简写）或 `targets` 列表、`strategy`（`round_robin`、`cost` 或
`failover`）、`session_affinity`（默认 `true`；参见[会话保持](https://gomodel.enterpilot.io/docs/zh/features/session-keeping.md)）、
`failover`（默认 `true`；参见[故障转移](https://gomodel.enterpilot.io/docs/zh/features/failover.md)）、`user_paths`、`description`、
`slowdown`（`0` 表示禁用继承的减速；有效系数范围为 `0.1` 到 `10`）以及 `enabled`。
目标的 `model` 可以指定另一个已声明的虚拟模型（参见[链式虚拟模型](#链式虚拟模型)）。
将目标留空即可在 `source` 选择器上声明访问策略。无效的声明（未知策略、缺失或自引用的目标、链中存在循环或链深度
超过 8 个虚拟模型，或目标的 `provider` 与任何已配置的提供商都不匹配，即拼写错误）会导致启动失败，
并给出列出已注册提供商名称的明确错误信息。

有两个宽松的例外，可确保有效的配置能够正常启动：

* 目标的 `provider` 已在 `providers:` 下声明但未成功注册（通常是因为当前环境中未设置其凭据）时，只会记录一条警告；
  在凭据可解析之前，该目标保持不可用。因此在多个环境之间共享的 `config.yaml` 仍能在所有环境中启动。
* 启动时永远不会检查目标*模型*是否存在：模型目录是异步加载的，因此可用性是运行时的问题，
  不可用的目标只会在解析时被跳过。

## 默认的模型可用性

除非某个 `virtual_models` 条目另有规定，否则每个提供商模型都是可用的。要反转这一默认行为，请设置：

```env theme={null}
MODELS_ENABLED_BY_DEFAULT=false
```

关闭默认可用后，提供商模型在有条目与之匹配之前都不可用。一个 `targets` 为空的条目就足够了，它充当纯粹的访问规则
（`enabled: true`，可选择附带 `user_paths`）。不可用的模型会使请求以 `400 model_access_denied` 失败，
并且不会出现在 `GET /v1/models` 中。重定向按其自身的 `enabled` 标志解析，但其目标会像直接调用一样被校验：
关闭默认可用时，也要为目标模型单独设置访问条目。如果该目标同时也是另一个重定向的源，请改用提供商级别或模型级别的策略：
在该源上设置的访问条目会替换在那里由控制台管理的重定向。

这与[仅暴露重定向](#仅暴露重定向)并非相反的设置：后者只会改变 `GET /v1/models` 列出的内容，
而默认可用性决定的是请求究竟可以调用哪些模型。

## 仅暴露重定向

要在 `GET /v1/models` 中隐藏提供商模型，请设置：

```env theme={null}
KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=true
```

启用后，GoModel 会从 `/v1/models` 返回已启用的重定向，而不是完整的提供商模型列表。
该设置也列在 `.env.template` 中。

## 列出不带前缀的模型 ID

默认情况下，`GET /v1/models` 返回带提供商限定的 ID，例如 `openai/gpt-5`。一些客户端会将您配置的模型与该列表进行校验，
但它们只认识不带前缀的名称，因此永远无法匹配。要改为列出不带前缀的 ID，请设置：

```env theme={null}
UNQUALIFIED_MODEL_IDS_AT_MODELS_ENDPOINT=true
```

`owned_by` 字段仍包含提供商名称。在两种模式下，对不带前缀的模型 ID 的请求都照常工作。

注意：当两个提供商公开相同的模型 ID 时（例如 `openai` 和 `azure` 上都有 `gpt-5`），只会列出不带限定的请求所路由到的
提供商（即最先注册的那个）。要将某个名称固定到特定提供商，请添加一个以该名称为源的虚拟模型：

```yaml theme={null}
virtual_models:
  - source: gpt-5
    target: azure/gpt-5
```

无论是在模型列表中还是在路由中，该重定向都优先于提供商条目。

## 覆盖模型

重定向可以“覆盖”一个模型：使用与现有模型相同的名称，并将其指向其他位置。这样您无需修改应用代码即可覆盖所请求的模型。

例如：

* 源：`anthropic/claude-opus-4-6`
* 目标模型：`anthropic/claude-sonnet-4-6`

您的应用可以继续发送 `anthropic/claude-opus-4-6`，而 GoModel 会将请求路由到 `anthropic/claude-sonnet-4-6`。

## A/B 测试

重定向非常适合进行短期的模型实验。将重定向从一个目标切换到另一个目标，然后比较应用的行为、延迟和用量。

例如，在一次测试中将 `smarter` 指向 Opus，在另一次测试中指向 Sonnet。您也可以用 `sonnet-4-6` 覆盖
`opus-4-6`，以检查相同的应用流程是否仍能正常工作。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.