# 用自定义 openai-compat 供应商让 Crush 跑在 Router One 上

> https://router.one/zh/integrations/crush 的 Markdown 镜像，供 AI 助手与爬虫使用。Router One 是 OpenAI 兼容的统一 LLM API 网关。
> 最后更新：2026-09-23

Crush 是 Charm 出品的终端编程 Agent：它在本机运行文件、Shell、LSP 与 MCP 工具，并把每一轮模型调用发给你配置的供应商。在 crushrc 中写 provider add router-one --type openai-compat --base-url https://api.router.one/v1，再把 model large 与 model small 固定到 router-one 的 ID。这个供应商会把 Crush 的调用以 POST /v1/chat/completions 发往 Router One，每一轮都是一条可计费、可追踪的请求；会话、工具、权限与上下文文件仍留在 Crush。本指南基于 2026 年 9 月 21 日发布的 Crush v0.96.1，该版本优先使用基于 Bash 的 crushrc；旧的 crush.json 仍可加载，但已弃用。

## 安装 Crush v0.96.1，并导出专用 Key

用 Homebrew 或 npm 安装 Crush，Windows 上用 Scoop；截至 2026 年 9 月 23 日，这三种方式都已提供 v0.96.1，而 winget 包仍停留在 0.93.1。v0.96.1 的 README 还列出了 Arch、Nix 与 FreeBSD 安装方式。crush --version 应输出 v0.96.1。为 Crush 创建一把设了 maxSpend 消费上限的 Router One Key，并在启动 Crush 的 Shell 中导出：crushrc 在 Crush 启动时执行，只能读到该环境中已经存在的变量。ROUTER_ONE_API_KEY 是本指南自定的变量名，Crush 读取它只是因为 crushrc 引用了它。crush dirs 会先列出配置目录（crushrc 放在这里），再列出 Crush 写入状态的数据目录。

`terminal`

```bash
brew install charmbracelet/tap/crush      # 或：npm install -g @charmland/crush
crush --version                           # crush version v0.96.1
export ROUTER_ONE_API_KEY="sk-your-router-one-key"
crush dirs                                # 先列配置目录，再列数据目录
# Windows（Scoop；winget 包可能滞后）：
#   scoop bucket add charm https://github.com/charmbracelet/scoop-bucket.git
#   scoop install crush
# PowerShell：$env:ROUTER_ONE_API_KEY = "sk-your-router-one-key"
```

## 把 Crush 配置到 Router One base URL

把代码块保存为配置目录下的 crushrc：macOS 与 Linux 是 ~/.config/crush/crushrc（设置了 XDG_CONFIG_HOME 时为 $XDG_CONFIG_HOME/crush/crushrc），Windows 是 %USERPROFILE%\.config\crush\crushrc。crushrc 是 Crush 内置解释器在启动时执行的 Bash；provider add、model add、model large 与 model small 都是 Crush 的内置命令，不是系统命令。router-one 是本地供应商 ID。--type openai-compat 选用 Crush 的 Chat Completions 客户端，--base-url 填带 /v1 的基础地址，不要加 /chat/completions。${ROUTER_ONE_API_KEY:?…} 写法会在变量缺失时让 Crush 报配置错误并停止。普通的 "$ROUTER_ONE_API_KEY" 会展开成空 Key，此时如果环境中导出了 OPENAI_API_KEY，Crush 的 OpenAI 客户端会改用它，把那把 Key 发给 Router One。model add 接受 <provider>/<id>，只在第一个斜杠处拆分，因此 router-one/anthropic/claude-sonnet-5 注册的目录 ID 是 anthropic/claude-sonnet-5，并原样发送。把两个 ID 换成 /models 中当前可用、详情页标明支持工具调用的聊天模型：large 槽位运行编程 Agent，small 槽位生成会话标题，并运行一个同样会调用工具的抓取子 Agent。代码块没有写 --context-window，因为它的值来自 /models 上各模型的详情页，而详情页显示的是取整后的缩写（如 200K、1.05M）：开始长会话前，请在每行 model add 后追加 --context-window，并把该值换算成整数、往小取（200K → 200000，1.05M → 1000000）；直接写 200K 会让 Crush 启动失败。项目中的 .crushrc 或 crushrc 会覆盖全局文件。两者都属于受信任代码，因此 Key 应放在环境变量里，不要写进会提交的文件。

`~/.config/crush/crushrc`

```bash
# ~/.config/crush/crushrc   (Windows: %USERPROFILE%\.config\crush\crushrc)
# Router One as a custom OpenAI-compatible provider -> POST /v1/chat/completions
provider add router-one \
  --name "Router One" \
  --type openai-compat \
  --base-url "https://api.router.one/v1" \
  --api-key "${ROUTER_ONE_API_KEY:?set ROUTER_ONE_API_KEY}"

# <provider>/<id>: Crush splits at the first slash, so the catalog ID keeps its own slash
# Append --context-window <model-page value as a whole number, e.g. 200000> to each line;
# without it Crush never auto-summarizes
model add router-one/anthropic/claude-sonnet-5 --name "Claude Sonnet 5 (Router One)"
model add router-one/anthropic/claude-haiku-4.5 --name "Claude Haiku 4.5 (Router One)"

# Pin both slots so titles and the fetch sub-agent bill this key too
model large router-one/anthropic/claude-sonnet-5
model small router-one/anthropic/claude-haiku-4.5
```

## 先用 crush run 测一次，-m 始终带 router-one/ 前缀

像下面这样通过管道使用时，crush models 以 provider/model 逐行列出 Crush 知道的全部模型（直接在终端运行时显示为按供应商分组的树）；筛选 router-one/ 即可确认 crushrc 已加载。crush run 会发出真实、计费的请求。除了回答本身，Crush 还会生成会话标题，所以一次提问通常在 Dashboard → Logs 中对应两条 POST /v1/chat/completions。在 v0.96.1 的本地模拟测试中，标题请求使用 small 模型，输出上限 40 tokens，不带工具；主请求使用 large 模型，带 stream: true、stream_options.include_usage 和 Crush 的工具定义。这条主请求包含 Crush 完整的系统提示词和工具定义（测试中为 26 个，JSON 约 49 KB），所以即使只是这句问候，也会在 large 模型上计费数千个输入 tokens。传 -m 时要保留 router-one/ 前缀，并同时传 --small-model。crush run 只有在存在同 ID 的供应商时才把第一段当作供应商名，因此只要配置了内置 OpenAI 供应商，-m openai/gpt-5.5 就会发往它。对 router-one 这类自定义供应商，只传 -m 而不传 --small-model，还会让本次运行的标题请求改用 large 模型。在 TUI 中按 ctrl+l 打开模型选择器。在那里做的选择会保存到数据文件 ~/.local/share/crush/crush.json（Windows 为 %LOCALAPPDATA%\crush\crush.json），它在全局 crushrc 之后加载，会覆盖其中的 model 行。Crush 自己也会写这个文件：model large 或 model small 指向供应商上未注册的 ID 时，它会不报错地回退到默认模型，并把回退结果存进去；若改了 crushrc 的 model 行却不生效，请删除数据文件里的 models 项。

`terminal`

```bash
crush models | grep '^router-one/'
crush run "Reply with one short greeting."
# 临时指定模型：两个槽位都加 router-one/ 前缀
crush run -m router-one/anthropic/claude-sonnet-5 \
  --small-model router-one/anthropic/claude-haiku-4.5 "Reply with one short greeting."
crush            # 交互模式；ctrl+l 打开模型选择器
```

## 让辅助请求也走同一把 Key

Crush 有两个模型槽位。large 模型运行 coder、plan 与 task Agent，并生成上下文摘要。small 模型生成会话标题，并运行 agentic_fetch：一个自带网页抓取、网页搜索、Sourcegraph、glob、grep 与 view 工具的子 Agent。标题请求的输出上限是 40 tokens，除非 model add 行设置了 --can-reason；请求失败或停在这个上限时，Crush 会改用 large 模型再请求一次，因此把输出花在推理上的 small 模型，可能让每个新会话多出一次计费的标题请求。未设置 model small 时由 Crush 代为选择：只有自定义供应商时沿用 large 模型，但已配置的内置供应商优先。在 v0.96.1 的模拟测试中，导出 OPENAI_API_KEY 且只把 model large 固定到 router-one 时，标题请求发给了内置 OpenAI 供应商并使用它的 Key，而不是 Router One。请像上面的配置一样，把 model small 也固定到 router-one 的 ID。若 Router One 应是唯一的供应商，再加一行 option default-providers false；此后 Crush 会忽略所有内置供应商，包括你在其他工作中使用的那些。

## 供应商类型决定请求路径

Crush 按 --type 选择客户端，只改 --base-url 不会在协议之间转换。Crush 的 README 建议：OpenAI 官方 API 之外的 OpenAI 兼容 API 使用 openai-compat，本指南用的就是它；--type openai 则会改用 Crush 的 OpenAI 客户端；自定义供应商不写 --type 时按 openai-compat 处理。如果还想用原生协议，再加一个供应商，使用独立的 ID、类型与模型列表。README 中 Anthropic 兼容示例的 base URL 以 /v1 结尾，在这个客户端里会把路径叠成 /v1/v1/messages。最后一列是 v0.96.1 模拟测试实际收到的请求，以及 Router One 的相关说明：

| --type | --base-url | 本地测试中 Crush 发出的请求及 Router One 说明 |
| --- | --- | --- |
| openai-compat（本指南） | https://api.router.one/v1 | 所有模型 ID 都发 POST /v1/chat/completions，带 Authorization: Bearer；这个端点服务当前所有聊天模型 |
| openai | https://api.router.one/v1 | ID 中含 gpt- 且后接 4 及以上的代数（如 openai/gpt-5.5）时发 POST /v1/responses，带 store: false 并请求 reasoning.encrypted_content；其他 ID 仍走 POST /v1/chat/completions。先确认模型详情页列出了 /v1/responses |
| anthropic | https://api.router.one（主机根地址，不带 /v1） | 发 POST /v1/messages，带 x-api-key 与 anthropic-version 请求头；仅限 Claude 系列与 DeepSeek 的 ID。base URL 以 /v1 结尾时实际请求变成 /v1/v1/messages |

## 上下文窗口、重试与费用记录

你在 model add 行里填的数值（--context-window、--default-max-tokens、--price-*）都是 Crush 端的设定，Router One 不会下发这些值。不写 --context-window 时模型窗口为 0，Crush 就不会对该会话自动摘要，每次请求重发的历史会不断增长，最终可能超出模型能接受的长度。请把模型详情页上的上下文窗口换算成整数填进 --context-window，并往小取（200K → 200000，1.05M → 1000000）；取小一些只会让摘要稍早触发。窗口大于 200,000 时，Crush 在剩余 20,000 tokens 时摘要；窗口不超过 200,000 时，在剩余 20% 时摘要；每次摘要都是 large 模型上的一次额外请求，option auto-summarize false 可以关闭它。--default-max-tokens 会成为主请求的输出上限；不设时，模拟测试中的 Chat Completions 请求不带 max_tokens 字段。遇到 408、409、429、5xx 与传输错误时，Crush 最多重试三次，间隔约 5、10、20 秒；Retry-After 响应头要求的时间小于 60 秒时则按其等待，因此一步操作在 Logs 中可能对应四条请求；400 与 402 会立即返回。v0.96.0 把默认请求超时提高到 120 秒（v0.96.1 的配置文档仍写 60 秒），对流式响应而言它是无活动超时；模型较慢时可设置 option request-timeout。Crush 因此中断的流式请求会在 Logs 中记为 HTTP 499 client_cancelled，只按已报告的用量计费。Crush 的会话费用由 --price-* 参数计算，不设则始终为零，因此计费仍以 Logs 为准。crush --debug 会把每个请求的 URL 与 JSON 请求体写入项目下的 .crush/logs/crush.log，可用 crush logs 查看。其中包含提示词和代码，调试结束后请删除该日志。

## Crush 该填哪个模型 ID？

从 /models 页复制精确的模型 ID，保留大小写、连字符和版本后缀，不要用展示名称代替。打开该模型的详情页，核对支持的 API 端点、上下文窗口和工具调用等能力，再与 Crush 当前选择的 provider 和功能对应。模型出现在目录里，不等于当前客户端能调用它的所有功能。建议为每个客户端或应用单独建 API Key，并设置 maxSpend 消费上限。

## Crush 用的是哪种 API 协议？

OpenAI 兼容描述的是接口格式，不能据此推断 Chat Completions（/v1/chat/completions）、Responses（/v1/responses）和 Anthropic Messages（/v1/messages）可以互换。先核对工具当前版本、provider 配置和实际请求路径，再查模型详情与 API 兼容性事实页。一次普通对话成功，也不能证明服务端工具、历史状态或文件编辑功能都受支持。

## 在 trace 里验证 Crush 的调用

先在 Crush 发出一次简单文本请求，再到 Dashboard → Logs 按时间、模型和 request_id 核对 trace：tokens、花费、延迟和状态码。成功后再分别验证流式输出、工具调用和多轮历史。失败时保留实际请求路径、完整错误消息及 request_id；没有对应日志时，先查客户端配置和网络，不能仅凭客户端报错认定是网关或上游故障。

## 常见问题

### Crush 能启动，但每个请求都返回 401，先查什么？

先确认 Key 是否真的传进了 Crush。crushrc 在 Crush 启动时展开变量。写成普通的 "$ROUTER_ONE_API_KEY" 而当前环境又没有这个变量时，v0.96.1 仍会以空 Key 启动，其 OpenAI 客户端会改读 OPENAI_API_KEY：本地模拟测试中，未设置 OPENAI_API_KEY 时请求完全不带 Authorization 请求头；导出了 OPENAI_API_KEY 时，请求会以 Bearer 形式带上这把 OpenAI Key，Router One 返回 401，而这把 Key 已经发出本机。旧的 crush.json 表现相同。上面使用的 ${ROUTER_ONE_API_KEY:?set ROUTER_ONE_API_KEY} 写法会让 Crush 在启动时直接报配置错误并停止，报错以 "executing shell config …/crushrc: exit status 1" 结尾，:? 后面的提示文字不会显示。在启动 Crush 的 Shell 中导出该变量，然后重启 Crush。如果请求头已经带上，再确认 Key 仍然有效、复制完整，并确认这次请求确实由 router-one 发出：从内置供应商选择的模型会使用那个供应商的 Key。

### 我已经有 crush.json，需要改成 crushrc 吗？

不需要。v0.96.1 仍会加载全局配置目录中的 crush.json，以及项目目录中的 .crush.json 或 crush.json，只是该格式已弃用，新选项只会加到 crushrc。等价写法是 providers.router-one：type 为 openai-compat，base_url 为 https://api.router.one/v1，api_key 为 "$ROUTER_ONE_API_KEY"（模拟测试中 $VAR 与 ${VAR} 两种写法都能展开），models 数组中的每个对象包含 id、name，以及可选的 context_window 与 default_max_tokens。crush.json 没有变量缺失保护：即使写成 ${ROUTER_ONE_API_KEY:?…}，v0.96.1 也只记录一条警告，并以空 Key 继续运行，所以启动 Crush 前请先导出该变量（见上面的 401 问题）。顶层 models 对象设置 large 与 small，每项的 provider 为 router-one，model 为目录 ID。可以加上 "$schema": "https://charm.land/crush.json" 引用官方发布的 schema。同一目录同时有两种格式时会合并，冲突时以 crushrc 为准；两者设置了相同的顶层键时，Crush 还会记录一条警告。

### 供应商能直接命名为 openai，或者对 GPT 模型用 --type openai 吗？

请使用独立的 ID，例如 router-one。openai 是 Crush 内置 OpenAI 供应商的 ID，provider add openai 只会覆盖它的地址与 Key；它自带的 gpt-5.5 等不带前缀的模型 ID 和默认模型都会保留，并以缺少目录所用 openai/ 前缀的形式发给 Router One。--type openai 是另一回事：它会把 openai/gpt-5.5 这类 ID 发往 /v1/responses，带 store: false，并请求推理摘要与加密推理内容，其他 ID 仍走 Chat Completions。Router One 对当前列出的 GPT 系列 ID 原生提供 /v1/responses，但依赖这些 Responses 字段之前，先用一次真实请求确认它们被接受。openai-compat 让所有模型都走 Chat Completions，也就是本指南全程使用的路径。

### 为什么模型选择器里只有我手动添加的模型？

因为这个供应商显式列出了模型。在 provider add 中加上 --discover-models true 后，Crush 每次加载配置时还会向 Router One 请求 GET /v1/models（限时 3 秒），并把返回的 ID 追加在你的模型之后。自动发现的条目只有 ID，没有上下文窗口、输出上限或价格，列表中还会包含不能用于聊天的图像生成 ID。router-one 供应商一个模型都没有时会自动触发发现，发现失败则 Crush 会丢弃这个供应商。对实际要用的槽位，显式写 model add 更可控。

### Crush 能通过网关用哪些模型？

选用当前目录中同时支持 Crush 所用端点和所需功能的模型。精确 ID、当前单价与能力以 /models 及模型详情为准；不要仅按 GPT、Claude 等系列名判断兼容性。客户端能列出模型，只说明它从自身配置或 GET /v1/models 读到了这个 ID，仍需验证实际调用。

### 能列出模型，但调用报 400 或 404，怎么办？

先记录实际请求路径和错误消息，再核对精确模型 ID。400 可能是参数、工具类型或模型与端点不匹配；404 可能是请求路径或资源不存在，不能直接判定模型下线。若错误提示 must be called via，按它指明的端点调整客户端 provider，或换用支持当前端点的模型。不要在所有工具里统一增删 /v1 或 /chat/completions。

### 中国大陆能直连吗？

能。网关在大陆可直连、无需 VPN，配置与全球环境完全一致。

### 报 401/402/403/429 怎么排查？

先到 Dashboard → Logs 核对请求及错误消息。401 查 Key 是否传入和有效；402 查钱包余额与 maxSpend；403 查 Key 权限和访问限制；429 查请求频率、token 限额及上游限流，按错误来源处理。保留 request_id，再按错误码速查页逐项排查。

## 相关页面

- 全部接入指南：https://router.one/zh/integrations
- Crush 的 API 错误排查：https://router.one/zh/llm-api-error-codes
- API 兼容性：端点与功能对照：https://router.one/zh/facts/api-compatibility.md
- Responses API 配置与限制：https://router.one/zh/codex-responses-api
- JetBrains AI Assistant 接入：https://router.one/zh/integrations/jetbrains-ai-assistant
- Copilot for Obsidian 接入：https://router.one/zh/integrations/obsidian-copilot
- Goose、OpenCode、Qwen Code、Aider 共用一把 Key 横评：https://router.one/zh/blog/goose-vs-opencode-vs-qwen-code
- 所有编程工具走同一个网关：https://router.one/zh/use-cases/ai-coding-tools
- 连接与 /v1 路径排查：https://router.one/zh/api-connection-troubleshooting
- Crush v0.96.1 README：自定义供应商：https://github.com/charmbracelet/crush/blob/v0.96.1/README.md#custom-providers
- Crush v0.96.1 官方文档：crushrc 命令参考：https://github.com/charmbracelet/crush/blob/v0.96.1/docs/config/README.md
- Crush v0.96.0 发布说明：默认超时改为 120 秒：https://github.com/charmbracelet/crush/releases/tag/v0.96.0
- Crush 的 crush.json JSON Schema：https://charm.land/crush.json
- 网关层负责什么：https://router.one/zh/llm-api-gateway
- OpenAI 兼容 API：https://router.one/zh/openai-compatible-api
- API 文档：https://router.one/zh/docs
- 本页规范地址：https://router.one/zh/integrations/crush
- 模型与每模型 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
