Crush 加自定义 provider:一条命令与两处 crushrc
如果你带着「找配置文件、改一个 JSON、填 baseURL」的肌肉记忆去接 Crush,第一步就会走岔:它的配置文件是 bash 风格的 crushrc,加服务商靠的是一条 provider add 命令,而不是往某个 JSON 数组里塞对象。 这个差别看着只是格式偏好,实际会影响你怎么放项目级配置、怎么让队友复现、以及配置能不能安全地进版本库。
这篇只讲一件事:在 Crush 里把一个自定义的模型服务商接进来,需要知道哪几个事实,以及这些事实的边界在哪。想先在几个终端 Agent 之间做取舍,看 终端 Agent 横评 和 开源终端 Agent 怎么选型;想看另一个终端 Agent 的子命令体系长什么样,看 OpenCode 的 CLI 命令。本篇不做横向排名,只把 Crush 这条路径讲透。
一、crushrc 是什么:bash 风格的配置,不是 JSON
先把概念对齐。这类工具的「配置文件」通常承担两件不同的事:一是声明你要用谁家的模型(接入方式),二是声明这个模型能干什么(模型能力)。很多人把两件事混成一件,结果配置填对了却发现工具用不起来,或者反过来,怀疑是模型不行、其实是端点没配对。
按 Crush 官方仓库 README(URL 见文末)截至 2026-08-07 的记载,它的配置文件叫 crushrc,形式是 bash 风格加上 Crush 内建命令,明确不是 JSON。这意味着它长得更像一份 shell 启动脚本:一行一条命令,命令带参数。
这个选择带来的直接后果是:你在终端里敲的命令,和你写进配置文件的内容,是同一套语法。JSON 派的工具(比如 Zed 的 settings.json、Gemini CLI 的 settings.json)是「结构声明」,你要先知道嵌套层级;YAML 派的工具(比如 Continue 的 config.yaml、aider 的 .aider.model.settings.yml)同理。命令派的好处是你可以先在终端里试通一条命令,确认能跑,再原样搬进 crushrc——试错和固化之间没有翻译损耗。
代价也在同一处:命令行语法没有 JSON Schema 那种结构约束,参数拼写错了是不是会被拦住、什么时候拦住,取决于实现,本篇不做描述,以你实际运行的结果为准。
二、provider add:一条命令加自定义 provider
README 里给出的自定义 provider 示例,原文是这样(下面这段是官方 README 的示例,未作改动):
provider add deepseek --type openai-compat \
--base-url "https://api.deepseek.com/v1"
拆开看三个部分:
provider add <名字>:deepseek是你给这个 provider 起的名字。--type openai-compat:声明这个端点走的是 OpenAI 兼容协议。所谓「OpenAI 兼容端点」,是指服务商把自己的接口做成和 OpenAI 那套请求/响应格式一样,客户端不用改代码就能换后端;这也是很多第三方模型服务采用的做法,展开可看 OpenAI 兼容端点是什么。--base-url:告诉 Crush 请求发到哪个地址。注意这个示例里的地址是带/v1的。
关于类型,README 写明:自定义 provider 必须是 OpenAI 兼容或 Anthropic 兼容 API。也就是说,Crush 认的是这两种协议方言,你手上那个端点如果两种都不是,这条路径就走不通——不是参数填法的问题,是协议层的问题。
README 同时列出了它支持的 provider:Anthropic、OpenAI、Gemini、Ollama 与自定义本地模型。Ollama 与本地模型出现在这份清单里,意味着把本地跑的模型接进来走的也是同一套机制。
需要说清的是:README 这一段给出的是加 provider 的命令示例。至于加完之后如何选模型、模型 ID 在哪里指定,本篇依据的这份记录里没有写出对应的命令形式,因此不写——这不等于 Crush 没有这些能力,请以官方文档与 --help 输出为准。
三、项目级与全局:两个位置,顺序怎么读
README 给出了 crushrc 的两个位置,并按优先级列出:
./.crushrc(项目级)~/.config/crush/crushrc(全局,Unix 类系统)
按这个排列顺序,项目级排在全局之前。对你实际意味着什么:一个仓库里可以放一份 ./.crushrc,把这个项目要用的 provider 固定下来,跟着代码走;个人长期习惯放全局那份。团队协作时,新人克隆仓库就带上了项目约定,不需要口口相传「你先去改一下你家目录里那个文件」。
两点必须诚实说明:
- README 只列了顺序,没有说明两份配置是「覆盖」还是「叠加」——即项目级存在时,全局那份是整份失效,还是逐项覆盖、其余保留。这两种语义在不同工具里都常见,本篇不替它下结论,请以官方文档为准,或者用一个最小配置实测一次。
- 全局路径那条标注的是 Unix 类系统。README 另外提到 Crush 跨平台(macOS / Linux / Windows / BSD / Android),但本篇依据的这份记录里没有给出 Windows 下的全局配置路径,所以不写。
顺带给个参照系:aider 的做法是 .aider.model.settings.yml 可以放四处(home 目录、git 仓库根目录、启动 aider 的当前目录、--model-settings-file 指定的路径),按顺序加载、后加载的优先。Gemini CLI 则把优先级写成一条完整链路:硬编码默认 → system defaults 文件 → user settings → project settings → system settings → 环境变量 → 命令行参数。三家的共同点是「越靠近你当下这次执行的,越说了算」;差别在于层数和是否显式写明合并规则。
四、横向对照:--base-url 在别家叫什么
「填一个端点地址」是接自定义服务商时绕不开的一步,本篇引用的这批产品里,Cline、Roo Code、Kilo Code、Continue、Zed、goose、Crush 都有对应的那一项,但字段名各不相同,串台就会白折腾半天。下面这张表里的字段名全部逐字取自各家官方文档(来源 URL 见文末),最后一列的「填错会怎样」标注为机制推断的,属于 OpenAI 兼容协议层面的通用道理,不是各家文档的原话。
| 配置项(逐字) | 出自 | 它是什么 | 填错会怎样 |
|---|---|---|---|
--base-url | Crush 的 provider add 命令参数 | 请求要发到的端点地址 | 机制推断:地址不对,请求根本到不了服务商,和模型能力无关 |
--type openai-compat | Crush 的 provider add 命令参数 | 声明该端点走 OpenAI 兼容协议 | README 写明自定义 provider 必须是 OpenAI 兼容或 Anthropic 兼容 |
./.crushrc | Crush 项目级配置路径 | 跟着仓库走的那份配置 | 放错目录即不生效;具体行为以实际运行为准 |
~/.config/crush/crushrc | Crush 全局配置路径(Unix 类系统) | 个人长期配置 | 同上 |
| Base URL | Cline / Roo Code / Kilo Code 的界面字段 | 同样是端点地址,但是表单里填 | Cline 与 Roo Code 文档都提示:这里不会是 https://api.openai.com/v1 |
apiBase | Continue 的 config.yaml | 覆盖默认 API 端点 | 机制推断:写成别家字段名等于没配 |
api_url | Zed 的 settings.json | 自定义 base URL | 同上 |
OPENAI_HOST | goose 的环境变量 | 自建 / 企业内部 OpenAI 兼容端点的主机(另有可选的 OPENAI_BASE_PATH) | 机制推断:goose 把主机和路径拆成两段,照抄别家那种一整条 URL 的写法会不对 |
关于要不要带 /v1:Crush README 的示例带了;Kilo Code 文档明确接受 https://api.provider.com/v1 和 https://api.provider.com/v1/chat/completions 两种形态,并说明第二种是给端点结构非标准的服务商和自建网关准备的;Zed 文档示例是 https://example.com/v1。结论是:以你的服务商文档写的那个地址为准,别按别家示例类推。
五、边界与代价
把配置做成命令,是有取舍的。以下几条讲清这条路径放弃了什么、哪些事它明确不管。
它不管模型能不能干活。 端点接通只解决「请求发得出去、回得来」,不解决模型是否具备 Agent 需要的能力。最典型的门槛是原生工具调用(function calling,即模型能按结构化格式请求调用外部工具,而不是吐一段自然语言让客户端猜)。Roo Code 官方文档在这一点上写得很硬,原话是 “Roo Code uses native tool calling exclusively. This is the only supported tool protocol — there is no XML-based fallback.”——这是 Roo Code 文档的说法,不是 Crush 的记载,我把它放在这里是提醒你:接入方式和模型能力是两件事,接通不等于能用。至于 Crush 对模型工具调用能力的具体要求,本篇依据的记录里没有对应描述,不写。
它不替你声明上下文窗口这类参数。 上下文窗口指模型单次请求能吞下的 token 上限。有些工具要你手填这个数:Cline 有 Context Window size,Roo Code 有 Context Window,Zed 在 available_models 里写 max_tokens,aider 用 .aider.model.metadata.json 注册 max_input_tokens / max_output_tokens。本篇依据的那份 Crush 记录里没有出现对应的项——这不等于 Crush 没有,只是本篇没有可引用的依据,请查官方文档。
模型列表怎么来,本篇没有依据。 同类工具里有两种做法:Kilo Code 在凭据有效时从 /v1/models 端点自动拉取模型列表,失败可手填;Zed 要在 available_models 里手写;Continue 在 models 块里手写。Crush 属于哪一种,记录里没有,不猜。
密钥怎么存,本篇也没有 Crush 的依据。 这件事各家差异很大:Zed 文档原话是 “Do not put API keys in settings.json.”,凭据走 provider 设置界面或环境变量(命名规则 <PROVIDER_NAME>_API_KEY),另一页写明 “Provider keys saved through Zed are stored in the system keychain, not in settings.json.”(keychain 即操作系统自带的凭据保管库);goose 走环境变量或 config.yaml;Gemini CLI 支持在 settings.json 里用 $VAR_NAME 或 ${VAR_NAME} 做环境变量插值——插值就是配置文件里只写变量名、运行时再去环境里取值,好处是配置能进版本库而密钥不进。Crush 的 crushrc 是 bash 风格,你自然会想到用同样的思路,但这属于你自己的工程判断,不是 README 的记载。密钥管理本身的做法看 API Key 安全管理。
它适合什么、不适合什么。 命令式配置适合「一条命令试通、原样固化」的工作流,也适合把项目约定塞进仓库;不适合需要把配置当成结构化数据去程序化生成、校验、diff 的场景——bash 风格的行不像 JSON/YAML 那样有现成的 schema 工具链。如果你的团队要做配置的集中下发和静态校验,这一点要先想清楚。
六、避坑清单
把别家的字段名搬过来。 为什么会踩:这几年你大概同时用着三四个工具,apiBase、api_url、Base URL、OPENAI_HOST、--base-url 在脑子里已经糊成了「那个填地址的」。怎么避:配之前先确认你现在配的是哪一家,只用那一家文档里出现的字段名;写完扫一眼,凡是从记忆里蹦出来的字段名都回文档核一遍。
照抄别人示例里的 /v1。 为什么会踩:示例地址带不带 /v1 各家不一样,Kilo Code 甚至同时接受两种形态。怎么避:以你的服务商 API 文档给的那个地址为准,一个字符不改地贴进去,而不是按某篇教程的形状去凑。
以为改完全局那份就万事大吉。 为什么会踩:项目目录下如果存在 ./.crushrc,它在 README 的优先级列表里排在全局之前,你改的是排在后面的那份。怎么避:排查配置不生效时,先 ls -a 看当前目录有没有 .crushrc,确认自己改的是真正生效的那份。
把「接通了」当成「能用了」。 为什么会踩:请求发得出去、能收到回复,看起来一切正常,但 Agent 一开始调工具就不对劲。怎么避:接入完成后单独验证一次带工具调用的任务,而不是只发一句「你好」;模型是否支持工具调用要查服务商文档,Roo Code 文档就明确建议先去服务商那边确认这一点。
在 Windows 上按 Unix 路径找全局配置。 为什么会踩:README 里 ~/.config/crush/crushrc 那条标注的是 Unix 类系统,而 Crush 本身跨平台。怎么避:Windows 下不要照搬这个路径,查官方文档确认对应位置;项目级的 ./.crushrc 是跨平台都成立的那一份,实在拿不准就先用项目级验证。
把配置文件直接提交进仓库而没管密钥。 为什么会踩:./.crushrc 天然就在仓库目录里,git add . 一把梭很容易带进去。怎么避:提交前确认这份文件里有没有明文凭据;把凭据留在环境变量里,配置文件只引用,是通用做法。另外 README 里还有一条你可能用得上:CRUSH_DISABLE_METRICS=1 用于关闭用量统计。
误以为这条路径能接任意端点。 为什么会踩:--type openai-compat 看起来很万能。怎么避:记住 README 的限定——自定义 provider 必须是 OpenAI 兼容或 Anthropic 兼容 API。协议不对就不是配置能解决的事,要么换网关做协议转换,要么换后端。
数据来源与核对日期
以下 URL 全部为本篇引用事实的官方来源,核对日期 2026-08-07。所有字段名、命令、路径均以核对当日各家官方文档/仓库的记载为准,请以官方文档最新版复核。
- Crush(
crushrc、provider add、--type openai-compat、--base-url、两处配置路径、支持的 provider 清单、CRUSH_DISABLE_METRICS=1、跨平台):https://raw.githubusercontent.com/charmbracelet/crush/main/README.md - Cline(Base URL 字段与提示):https://docs.cline.bot/provider-config/openai-compatible
- Roo Code(Base URL 字段、Context Window、原生工具调用原话):https://roocodeinc.github.io/Roo-Code/providers/openai-compatible
- Kilo Code(Base URL 两种形态、
/v1/models自动拉取):https://kilo.ai/docs/providers/openai-compatible - Continue(
config.yaml、apiBase、models块):https://docs.continue.dev/reference - aider(
.aider.model.settings.yml四处位置与加载顺序、.aider.model.metadata.json的max_input_tokens/max_output_tokens):https://aider.chat/docs/config/adv-model-settings.html - Zed(
api_url、available_models、max_tokens、API key 与 keychain 的两句原话):https://zed.dev/docs/ai/use-api-access 与 https://zed.dev/docs/ai/configuration - goose(
OPENAI_HOST、OPENAI_BASE_PATH、config.yaml):https://goose-docs.ai/docs/getting-started/providers/ - Gemini CLI(
settings.json位置与优先级链路、$VAR_NAME环境变量插值):https://google-gemini.github.io/gemini-cli/docs/get-started/configuration.html
本篇没有写以下内容,请直接以各产品官方文档为准:任何产品的价格、免费额度、订阅档位、限速数字;版本号与发布日期;完整的模型清单与模型 ID 推荐;各产品界面长什么样、报错文案是什么、什么时候会拦住你——这些要么会过时,要么本次没有可引用的一手依据。文中标注为「机制推断」的几处,是 OpenAI 兼容协议层面的通用道理,不是某家文档的原话,请按你自己的实测结果修正。
延伸阅读:同一组里的 goose 接自建端点、Gemini CLI 配置不生效;接完之后照 接完自定义模型别急着干活 逐项过一遍,才算真接通。