> https://router.one/zh/blog/goose-vs-opencode-vs-qwen-code 的 Markdown 镜像，供 AI 助手与爬虫使用。Router One 是 OpenAI 兼容的统一 LLM API 网关。
> 发布：2026-09-15 · 作者：Router One Team

# Goose、OpenCode、Qwen Code、Aider 横评：开源编程 agent 共用一把网关 Key

_Goose、OpenCode、Qwen Code、Aider 接入 Router One 对照：各自用哪个变量或配置指向网关、走哪种协议、哪些能力留在工具侧，一把 Key、每次请求一条 Trace。_

四个住在终端里的开源编程 agent，同一个问题：把模型调用改走 Router One 之后，到底变了什么？工具本身没有变——**Goose** 从 CLI 或桌面应用运行扩展（MCP 服务器）和会话；**OpenCode** 把模型 provider 当配置来处理；**Qwen Code** 用 `/auth` 和 `/model` 切换 provider 与模型；**Aider** 编辑文件并把每次改动提交到 git。变的是底下那次请求：一把 `sk-` Key，[/models](https://router.one/zh/models) 里的精确模型 id，以及每次模型请求在 Dashboard → Logs 里的一条 Trace——模型、tokens、花费、延迟、状态和 request_id。网关负责请求，循环仍归工具。

一句话结论。**Goose**：内置 OpenAI provider，`OPENAI_HOST` 填主机根地址、`OPENAI_BASE_PATH` 保持默认；或者写一个自定义 provider 的 JSON 文件。**OpenCode**：`opencode.json` 里一段基于 `@ai-sdk/openai-compatible` 的 provider 配置，模型以 `router-one/<id>` 选用。**Qwen Code**：`.qwen/.env` 里三个 `OPENAI_*` 变量，或者在 `modelProviders.openai` 下每个模型一条。**Aider**：`OPENAI_API_BASE`、`OPENAI_API_KEY`，再加 `openai/` 前缀规则。四者发出的都是 Chat Completions，所以目录里的每个聊天模型从任何一个工具都能到达。

## 对照表

| 工具 | 安装与运行位置 | 怎样指向网关 | 实际协议 | 能到达的目录系列 | 留在工具侧的部分 |
| --- | --- | --- | --- | --- | --- |
| Goose | CLI（Homebrew 或下载脚本）和桌面应用 | `OPENAI_HOST=https://api.router.one`、`OPENAI_API_KEY`、`GOOSE_PROVIDER=openai`、`GOOSE_MODEL=<id>`；或 `~/.config/goose/custom_providers/` 下的一个 JSON 文件 | Chat Completions（`OPENAI_BASE_PATH` 默认 `v1/chat/completions`）；gpt-5、gpt-6 与 o 系列名称走 `/v1/responses` | 全部聊天模型 | 扩展（MCP 服务器）、`GOOSE_MODE` 工具审批、会话、`GOOSE_MAX_TURNS` |
| OpenCode | 终端 TUI、桌面应用或 IDE 扩展（npm、Homebrew 或安装脚本） | `opencode.json` 的 `provider.router-one`：`npm` 填 `@ai-sdk/openai-compatible`，`options.baseURL` 带 `/v1`，`options.apiKey` 用 `{env:ROUTER_ONE_API_KEY}`，`models` 逐个列出 | Chat Completions（换成 `@ai-sdk/openai` 就是 Responses） | 全部聊天模型；可选再加一条 `@ai-sdk/anthropic` provider 让 Claude id 走 `/v1/messages` | 工具、`mcp` 服务器、`permission` 规则、agent |
| Qwen Code | Node.js 22+ 的 CLI（npm 或 Homebrew） | `.qwen/.env` 里的 `OPENAI_API_KEY`、`OPENAI_BASE_URL=https://api.router.one/v1`、`OPENAI_MODEL`；或 `~/.qwen/settings.json` 的 `modelProviders.openai[]` | 经官方 OpenAI Node SDK 发送 Chat Completions（`openai-responses` 发往 `/v1/responses`） | 全部聊天模型；Responses 原生服务 GPT 系列与 DeepSeek id | 内置工具、`/mcp`、`/approval-mode`、`/resume`、`/compress` |
| Aider | Python CLI（`aider-install`），在你的仓库目录里运行 | `OPENAI_API_BASE=https://api.router.one/v1`、`OPENAI_API_KEY`、`aider --model openai/<id>` | 经 LiteLLM 发送 Chat Completions | 全部聊天模型 | 仓库地图、git 自动提交、`/undo`、`/diff`、生成提交信息的 `--weak-model` |

四者说的都是 Chat Completions，而 `/v1/chat/completions` 服务目录里的所有聊天模型——GPT、Claude、Gemini、Grok 和 DeepSeek 系列——所以同一个 `anthropic/claude-sonnet-5` 或 `google/gemini-3.8-flash` id（连同自带前缀）在下面每份配置里都能用。最后一列是边界：里面没有一项跑在网关上。唯一的例外是 Goose：base path 保持默认时，它的 OpenAI provider 会自行把 gpt-5、gpt-6 与 o 系列名称发到 `/v1/responses`，而 Router One 对当前上架的 GPT 系列 id 原生提供该端点，所以设置本身不用改。

## Goose：OPENAI_HOST 填主机根地址，GOOSE_MODEL 填精确 id

Goose 内置的 OpenAI provider 可以连接「任何 OpenAI 兼容端点」，文档对代理和网关的写法说得很明确：`OPENAI_HOST` 填不带尾部路径的根地址，`OPENAI_BASE_PATH` 填端点实际服务的路径，而它的默认值 `v1/chat/completions` 正是需要的那个——所以主机根地址 `https://api.router.one` 就是全部设置。`GOOSE_PROVIDER` 和 `GOOSE_MODEL` 两个环境变量会在当前进程里覆盖配置文件，`GOOSE_MODEL` 原样发出，所以填目录里的精确 id。还有一条规则叠在默认值之上：名字匹配 gpt-5、gpt-6 或 o 系列的模型（`openai/gpt-5.5` 就符合），Goose 的 OpenAI provider 会按 Responses 请求发到 `/v1/responses`（`store` 为 false），其他 id 一律发 Chat Completions；如果自定义的 `OPENAI_BASE_PATH` 含有 `responses`，所有模型都会被强制走 Responses，Claude、Gemini 和 Grok 的 id 会收到 HTTP 400，所以这个路径不要动。

```bash
export OPENAI_HOST=https://api.router.one
export OPENAI_API_KEY=sk-your-router-one-key
export GOOSE_PROVIDER=openai
export GOOSE_MODEL=<exact-model-id-from-/models>

goose session
```

`goose configure` 会把 provider 和模型写进 `~/.config/goose/config.yaml`、把 Key 存进系统密钥库，但文档写明它不接受自定义模型名——`anthropic/claude-sonnet-5` 这类目录 id 不在它的列表里——所以 `GOOSE_MODEL` 要在环境变量里设，或直接改 `config.yaml`。另一条路是自定义 provider：在 `~/.config/goose/custom_providers/` 放一个 JSON 文件，`engine` 填 `openai`，`base_url` 填完整的 chat-completions URL，`api_key_env` 填存放 Key 的变量名，再加一个 `models` 数组；之后它会出现在 provider 列表里，`goose run --provider <name>` 可为单次运行选用它。桌面版在 Settings → Models → Configure providers → Add Custom Provider 里填同样几项，类型选「OpenAI Compatible」。

**坑在这里。** 404 说明 `OPENAI_BASE_PATH` 与端点对不上——保持默认即可。401 且提示 `No api key passed in` 说明 Key 没被加载：goose 不从 `config.yaml` 读取 provider 的 Key，环境变量则优先于已存储的密钥。goose 依赖工具调用，模型要选支持工具调用的。指南：[Goose 接入 Router One](https://router.one/zh/integrations/goose)。

## OpenCode：一段 provider 配置，模型写成 router-one/<id>

OpenCode 从 `opencode.json` 读取自定义 provider——项目里一份，或全局的 `~/.config/opencode/opencode.json`。配置块里的 `npm` 指定说哪种协议的包：文档把 `@ai-sdk/openai-compatible` 对应到 `/v1/chat/completions`，把 `@ai-sdk/openai` 对应到 `/v1/responses`，所以用前者。`options.baseURL` 填带 `/v1` 的地址，`options.apiKey` 用 `{env:变量名}` 语法读 Key，想进选择器的每个模型都是 `models` 里的一个键——精确的目录 id——选用时写成 `provider-id/model-id`。

```json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "router-one": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Router One",
      "options": {
        "baseURL": "https://api.router.one/v1",
        "apiKey": "{env:ROUTER_ONE_API_KEY}"
      },
      "models": {
        "<exact-model-id-from-/models>": { "name": "<选择器里显示的名称>" }
      }
    }
  },
  "model": "router-one/<exact-model-id-from-/models>"
}
```

TUI 里的 `/models` 命令列出你声明的模型，顶层 `model` 字段固定其中一个。其余能力都留在 OpenCode：`mcp` 下的 MCP 服务器（本地 `command` 或远程 `url`），文档写明其工具「自动与内置工具一起提供给模型」；`permission` 规则把每个动作——`edit`、`bash`、`webfetch`、代表子 agent 的 `task`——判定为 `allow`、`ask` 或 `deny`，agent 级规则优先。

**坑在这里。** model 字符串里的 provider id 必须与 `provider` 下的键名一致；没在 `models` 里声明的模型对 OpenCode 来说不存在；启动 `opencode` 的 shell 里没有该变量时，`{env:ROUTER_ONE_API_KEY}` 会变成空字符串而不是报错。想让 Claude id 走原生 Messages 路径，就用另一个 id 再加一条 `@ai-sdk/anthropic` 的 provider，base URL 同样填 `/v1`——网关接受该包发送的 `x-api-key` 头。指南：[OpenCode 接入 Router One](https://router.one/zh/integrations/opencode)；与两个厂商 CLI 的对比见 [OpenCode vs Claude Code vs Codex CLI](https://router.one/zh/blog/opencode-vs-claude-code-vs-codex-cli)。

## Qwen Code：.qwen/.env 里三个变量，或每个模型一条 modelProviders

Qwen Code 的 OpenAI 兼容认证类型是 `openai`，读取 `OPENAI_API_KEY`、`OPENAI_BASE_URL` 和 `OPENAI_MODEL`（别名 `QWEN_MODEL`）。base URL 带 `/v1`，请求经官方 OpenAI Node SDK 发出，协议就是 Chat Completions。文档推荐把这些变量放在项目的 `.qwen/.env` 里——与其他工具的变量隔离，并且不要提交到 git。它只加载找到的第一个 `.env` 文件（先 `.qwen/.env`、`.env`，再是 `~` 下的同名两个），shell 里 `export` 的值覆盖它们全部。

```bash
# .qwen/.env
OPENAI_API_KEY=sk-your-router-one-key
OPENAI_BASE_URL=https://api.router.one/v1
OPENAI_MODEL=<exact-model-id-from-/models>
```

想在 `/model` 选择器里保留多个目录 id，就在 `~/.qwen/settings.json` 的 `modelProviders.openai` 下声明——每条带 `id`（发给 API 的模型 id）、显示用的 `name`、`envKey`（存放 Key 的变量名，不是 Key 本身）和 `baseUrl`——并配上 `security.auth.selectedType: "openai"` 和一个与某条 `id` 一致的 `model.name`；运行中的会话会直接读到修改，`/model <model-id>` 立即切换。

**坑在这里。** `openai-responses` 是另一种认证类型，以直接 HTTP 请求发往 `/v1/responses`——这个端点在 Router One 上只对当前上架的 GPT 系列和 DeepSeek id 原生可用，其他 id 会在任何模型运行之前收到 HTTP 400，所以 Claude、Gemini、Grok 的 id 要放在 `openai` 下。`id` 和 `baseUrl` 都相同的两条只保留第一条，并给出警告。`generationConfig.maxRetries` 触发的重试是独立请求，各有各的 Trace。审批模式是工具自己的：`/approval-mode yolo` 会不经确认地执行 shell 命令、文件写入和网络请求，文档要求只在可信或可丢弃的环境里使用。指南：[Qwen Code 接入 Router One](https://router.one/zh/integrations/qwen-code)。

## Aider：OPENAI_API_BASE、OPENAI_API_KEY 和一层 openai/ 前缀

Aider 关于 OpenAI 兼容 API 的文档就是两个变量加一个参数：`OPENAI_API_BASE` 填带 `/v1` 的端点，`OPENAI_API_KEY`，以及 `aider --model openai/<model-name>`。Aider 通过 LiteLLM 连接模型，`openai/` 前缀是协议选择器——「对 `OPENAI_API_BASE` 使用 OpenAI 兼容协议」——并且会被吃掉，所以后面原样接目录 id。

```bash
export OPENAI_API_BASE=https://api.router.one/v1
export OPENAI_API_KEY=sk-your-router-one-key

aider --model openai/<exact-model-id-from-/models>
```

因为只会吃掉一层前缀，GPT 系列的 id 要写两遍——`openai/openai/gpt-5.5`——Claude 的 id 则连同自带前缀写一遍——`openai/anthropic/claude-sonnet-5`。命令行上 `--openai-api-base` 和 `--openai-api-key` 起同样的作用，Aider 从 git 根目录加载的 `.env` 文件也可以放这两个变量。Aider 拿到响应之后做的事都留在 Aider：每次改动请求都附带仓库地图（`--map-tokens`，默认 1k tokens），每次编辑一次 git 提交，以及 `/undo` 和 `/diff`。

**坑在这里。** 启动时会看到 `Unknown context window size and costs, using sane defaults`——Aider 没有网关 id 的元数据，文档说这个警告可以忽略，因此它显示的费用不是账单。提交信息是单独的模型请求：Aider 把 diff 和聊天记录发给 `--weak-model`，想让这些请求也走同一把 Key、进同一份 Logs，就给这个参数同样的 `openai/` 写法；`--no-auto-commits` 可关闭自动提交。指南：[Aider 接入 Router One](https://router.one/zh/integrations/aider)。

## 网关做什么、不做什么

Router One 负责模型请求。每一次请求它都在 Dashboard → Logs 记一条 Trace——模型、输入输出 tokens、花费、延迟、HTTP 状态、request_id——并执行 Key 上的上限：`maxSpend`，以及对付失控循环的 `rateLimit` 和 `tokenLimitTpm`。id 发到不服务它的端点，会在调用任何模型之前被 HTTP 400 拒绝；各端点接受和拒绝的清单见 [API 兼容性事实页](https://router.one/zh/facts/api-compatibility.md)，其余状态码见[错误码页](https://router.one/zh/llm-api-error-codes)。

它不执行工具、不保存会话，也不审批动作。Goose 的扩展、OpenCode 的 `permission` 规则和 MCP 服务器、Qwen Code 的审批模式、Aider 的 git 提交——全都跑在你的机器上；网关每一轮只看到一次 Chat Completions 请求，中间发生的事它看不到。Trace 不显示上游名称或内部路由尝试，单独一个 HTTP 200 也不能证明流已经完整结束（见[流式输出](https://router.one/zh/llm-streaming)）。账本记什么，见[成本追踪页](https://router.one/zh/llm-cost-tracking)；厂商 CLI 的两个变量配置见 [CLI 配置指南](https://router.one/zh/docs/guides/cli-setup)、[Claude Code 国内接入](https://router.one/zh/claude-code-china)和 [Codex CLI 国内使用](https://router.one/zh/codex-china)。

## 给一次编程会话定预算

上面每个工具都有循环上限，但没有一个是花费上限：`GOOSE_MAX_TURNS` 限制无人干预时的轮数（默认 1000），OpenCode 的 `doom_loop` 权限拦截重复的相同调用，Qwen Code 的 `maxRetries` 限制单次请求的重试次数。重试和工具调用循环叠在这些上限之上：每一次到达网关的尝试都是独立的请求、Trace 和费用，agent 读到的每个工具结果都会作为新请求再发给模型。工具侧的费用数字——Aider 的估算、TUI 里显示的任何数字——都不是你实际被扣的钱。

真正止损的上限在 Key 上：一个工具一把 Key，各设 `maxSpend`，失控的会话到顶即停、返回 402，钱包和其他 Key 不受影响。按 request_id 核账——按时间窗与精确模型过滤 Logs，把条数对上会话的轮数，保留失败 Trace 的 request_id。被取消的流式请求既不会被悄悄扣费，也不会被悄悄记零：在 `POST /v1/chat/completions` 与 `POST /v1/responses` 上，客户端中途取消的请求——例如一轮进行到一半时按下 Ctrl-C——记为 HTTP 499 `client_cancelled`，只按上游实际报告的用量计费。

到 [router.one](https://router.one/zh) 为每个工具建一把 Key，各设上限；[/integrations](https://router.one/zh/integrations) 列出全部接入指南，[所有编程工具走同一个网关](https://router.one/zh/use-cases/ai-coding-tools)是总览。换完配置之后工具仍然报连接错误，说明端点其实没有换掉——先看 [API 连接排错](https://router.one/zh/api-connection-troubleshooting)。

## 常见问题

**哪个工具用一把 Key 能到达最多模型系列？**
四个一样多：每个都发 Chat Completions（只有 Goose 会把 gpt-5、gpt-6 与 o 系列名称改送 /v1/responses，网关对 GPT 系列 id 原生提供该端点），而 /v1/chat/completions 服务目录里的所有聊天模型——GPT、Claude、Gemini、Grok 和 DeepSeek 系列。区别只在 id 填在哪里：Goose 的 GOOSE_MODEL、OpenCode models 里的键、Qwen Code 的 OPENAI_MODEL 或 modelProviders 的 id、Aider 的 openai/ 前缀之后。

**一把 Key 能同时接四个工具吗？**
能。本文每份配置都接受同一把 Router One Key，每次调用使用同一个钱包。分开的 Key 让每个工具各有自己的 maxSpend 上限，一个失控的会话花不掉另一个工具要用的钱。

**同一个 GPT 系列 id 为什么在每个工具里写法不同？**
每个工具对同一个实际发送的 id 有自己的命名规则：OpenCode 前面加自己的 provider id（router-one/openai/gpt-5.5），Aider 吃掉一层 openai/（openai/openai/gpt-5.5），Goose 和 Qwen Code 原样发送目录 id（openai/gpt-5.5）。Logs 的 model 一列显示的是实际发出的 id，核对它是不是完整的目录 id。

**网关看得到工具、MCP 服务器或会话吗？**
看不到。扩展、MCP 服务器、权限、审批模式、会话和 git 提交都在你的机器上运行。网关每一轮收到一次 Chat Completions 请求，记录 Trace、执行 Key 上的上限、返回响应；请求体里的工具定义和工具结果按 token 计量，其中没有任何东西会被执行。

## 相关页面

- 本页规范地址：https://router.one/zh/blog/goose-vs-opencode-vs-qwen-code
- Claude Code 中国：https://router.one/zh/claude-code-china
- 全部博客文章：https://router.one/zh/blog
- 模型与每模型 token 价格：https://router.one/zh/models （markdown：https://router.one/zh/models.md ）
- 定价：https://router.one/zh/pricing
- API 文档（markdown）：https://router.one/zh/docs.md
- 公司事实（markdown）：https://router.one/zh/facts/company.md
