Zed 为什么不让把密钥写进 settings.json:两条路怎么选

2026-08-07

如果你只带走一句话:Zed 把「模型怎么接」和「凭据放哪」当成两件事分开设计,settings.json 只负责前者,密钥被明确赶出这个文件。 这不是一句风格建议,是官方文档里的原话——截至 2026-08-07,Zed 文档写的是 “Do not put API keys in settings.json.”(这是官方文档的说法,本文只做转述)。理解了这条分界,你在 Zed 里配自定义模型时遇到的大部分困惑——“我把 key 写哪去了""为什么同事拉了我的配置还是用不了”——都会变成有明确答案的问题。

本站已经有几篇讲密钥治理的文章,分工是这样的:API key 的安全管理 讲的是通用的存放与权限原则,API 密钥轮换 讲的是换密钥的节奏和不停机切换,日志里的敏感信息 讲的是密钥怎么从日志侧漏出去。这篇不重复那些,只做一件事:把 Zed 这一个客户端的凭据存放机制讲透,讲清它的两条路各自适合什么场景、代价是什么。如果你想先看各家编辑器接自定义模型的整体差异,可以从编辑器接入自定义 API 那篇入手。

一、这块到底在解决什么问题

先把两个容易混为一谈的东西分开。

接入方式,指的是客户端怎么找到你的模型服务:往哪个地址发请求、用什么协议、有哪些模型可选。模型能力,指的是那个模型本身能干什么:上下文窗口多大、支不支持工具调用、能不能读图。这里再补两个词的意思:上下文窗口是一次请求里模型能同时看见的 token 总量上限,历史对话加上你新发的内容都算在里面;工具调用(也叫 function calling)是模型按约定的结构化格式吐出”我要调用某个函数、参数是这些”,再由客户端去执行并把结果回喂给模型。这两件事在配置文件里经常挨着写,但性质完全不同——前者是连接参数,后者是你对模型的声明。

凭据是第三件事。它既不是连接参数,也不是能力声明,它是一段不该被复制、不该进版本库、不该出现在截图里的秘密。很多客户端图省事,把这三件事塞进同一个配置文件;Zed 的选择是把第三件拆出去。

这个拆分带来的直接好处是:settings.json 变成了一个可以放心分享的文件。你可以把它贴进内部 wiki、提交进仓库、发给同事排查问题,里面不会带出任何凭据。代价也很明确——文件本身不再自足,光有它跑不起来,这一点后面会专门讲。

顺便解释两个后面要反复出现的词。keychain(系统钥匙串)是操作系统提供的凭据保管服务,应用把秘密交给它,由系统负责加密存储和访问控制,应用自己不落地明文文件。环境变量是进程启动时从操作系统环境里读到的键值对,它活在进程的内存里,不落在项目目录中——这也是它常被用来传密钥的原因。

二、settings.json 里该写什么:逐字段拆开

先说清”OpenAI 兼容端点”这个说法:它指的是服务端把接口做成和 OpenAI 那套 HTTP 请求/响应格式一致,于是任何按那套格式说话的客户端,只要换个地址和密钥就能连上,不需要为每家服务商单独写适配代码。Zed 文档给出的自定义 OpenAI 兼容端点配置示例长这样(以下为官方文档示例,原样引用):

{
  "language_models": {
    "openai_compatible": {
      "my-provider": {
        "api_url": "https://example.com/v1",
        "available_models": [
          {
            "name": "my-model",
            "display_name": "My Model",
            "max_tokens": 128000
          }
        ]
      }
    }
  }
}

注意这里面没有任何一处放密钥的位置。这不是示例省略了,是设计如此。

文档对各字段的说明是:api_url 用来自定义 base URL;available_models 是模型数组,每项含 name(模型标识)、display_name(界面显示名)、max_tokens(上下文窗口上限);另有 capabilities 对象控制能力开关,文档提到的开关包括 tools、images、parallel_tool_calls 等。

字段文档里说它是什么你要自己判断的地方出处
api_url自定义 base URL填服务商给你的端点。文档示例是 https://example.com/v1 这种带 /v1 的形态,你的服务商是不是同样形态,以对方文档为准Zed 官方文档(URL 见文末)
available_models模型数组这里要手写,不是自动发现。你打算用哪几个模型,就得逐个列出来同上
name模型标识按”模型标识”这个语义推断,它应当与服务端认的模型 ID 对得上;文档没有逐条说明客户端如何使用该值,以官方文档为准同上
display_name界面显示名从字段名看是给界面显示用的;具体允许写什么、是否影响请求,文档未逐条说明,以官方文档为准同上
max_tokens上下文窗口上限由你填。填多少合适取决于该模型服务端的真实上限,以服务商文档为准同上
capabilities能力开关对象,含 tools、images、parallel_tool_calls 等你声明的能力得和模型实际支持的对上同上
<PROVIDER_NAME>_API_KEY环境变量命名规则;provider 名为 my-provider 时对应 MY_PROVIDER_API_KEY变量名由你的 provider 名推导,改名就得同步改同上
disable_ai关闭全部 AI 功能,写法 "disable_ai": true想彻底关掉时用同上

关于 max_tokens 这类”要你自己填”的项,多说一句机制层面的判断(这是 OpenAI 兼容协议的通用道理,不是 Zed 文档的说法):客户端拿不到服务端的真实上限,只能信你写的数。写得比服务端实际能力大,超出部分的请求会在服务端侧被拒;写得小,你就用不满这个模型本来能吃下的上下文。所以这个数字要去服务商那边查,不要照抄示例里的 128000。

三、两条凭据路径:keychain 与环境变量

文档给出的凭据路径是两条:provider 设置界面,或环境变量

第一条:交给 Zed,落进系统 keychain。 configuration 页的原话是 “Provider keys saved through Zed are stored in the system keychain, not in settings.json.”(官方文档说法)。也就是说你通过 Zed 保存的 provider key,最终躺在操作系统的钥匙串里,不在配置文件里。文档提到的相关命令有 agent: open settings(配 LLM provider)、zed: open settingszed: open settings file。具体界面长什么样、点哪一步,以你实际看到的界面为准,这里不做描述。

这条路的特点,按 keychain 这类系统级凭据存储的一般机制推断(文档只写了”存在系统 keychain 里”这一句,没有展开):密钥和机器绑定。它跟着这台机器的系统账户走,不跟着项目走,也不跟着你的 dotfiles 仓库走——换台机器多半得重新输一次。具体的同步与迁移行为取决于你所用操作系统的钥匙串实现,以系统与官方文档为准。

第二条:环境变量,命名规则是 <PROVIDER_NAME>_API_KEY 文档给的对应关系很直白:provider 名为 my-provider 时,对应环境变量是 MY_PROVIDER_API_KEY。这里有个实操细节值得留意——provider 名在 settings.json 里是那个 JSON 对象的键(示例中的 my-provider),环境变量名由它推导而来。你哪天觉得 my-provider 这名字太随便想改成别的,环境变量名也得跟着改,否则连不上。

这条路的特点是:密钥跟着 shell 环境走。它天然适合 CI、容器、远程开发机这类”没人坐在前面点界面”的场景,也适合你已经有一套集中管理环境变量的做法(比如从密钥管理服务注入)。代价是环境变量本身有一堆坑——最常见的是你在某个终端里 export 了,换个终端或者从图形界面启动应用就读不到了,这类问题在环境变量丢失怎么排查里有系统说法。

两条路怎么选,给个可操作的判断:你自己的开发机、只有你一个人用、图省事——走 keychain;要在多台机器/容器/CI 里复现同一套配置,或者密钥本来就由外部系统统一注入——走环境变量。这两条不是互斥的哲学之争,只是两种生命周期不同的存放位置。

四、这对团队共享配置意味着什么

现在把视角拉到团队。

一份 settings.json,里面有 api_url、有 available_models 里逐个列好的模型、有 capabilities 开关——这份文件里没有秘密,所以它可以进仓库、可以进内部文档、可以在群里直接贴。新同事拿到它,api_url 和模型清单一次到位,只差自己那份凭据。这是”密钥不进配置文件”最实际的收益:配置的可分享性和凭据的私有性被分开了,不用再靠”记得把 key 那行删掉”这种全靠人肉的纪律。

但也要看清它没解决的部分。Zed 的设计是把密钥移出配置文件,不是引用进配置文件。这两者有区别,对比一下就清楚了:

  • Gemini CLI 的做法是在 settings.json 里支持环境变量插值,写 $VAR_NAME${VAR_NAME},加载时自动解析。所谓”插值”就是配置文件里写一个占位符,运行时替换成环境变量的实际值——这样配置文件能进版本库,而密钥不进。
  • goose 的做法是走环境变量或 config.yaml,密钥各家用各自的变量名。
  • Zed 则是 settings.json 里根本不给密钥留位置,凭据要么在 keychain,要么在按规则命名的环境变量里。

这三家解决的是同一个矛盾——配置想共享、密钥不想共享——路径不同。Zed 这条路更”干净”(文件里连引用都没有),代价是配置文件不再是完整可执行的描述:你把它交给同事,还得口头补一句”你自己的 key 记得在设置里填 / 或者 export 一个 XXX_API_KEY”。这句口头补充如果没人写进文档,就是新人上手卡住的常见原因。

再说一句 Zed 的接入全景,帮你判断自定义 provider 是不是必要的那条路。文档列出的模型接入路径共五类:Zed 托管模型、自带 API key、复用已有订阅、网关(OpenRouter / Vercel AI / Amazon Bedrock)、本地模型(Ollama / LM Studio / 自托管)。上面讲的 openai_compatible 属于其中偏手工的那一档——当你的端点不在现成清单里时才需要它。

五、边界与代价:这个做法放弃了什么

把话说全,这个设计有明确的取舍。

放弃了配置的自足性。 一份配置文件不再能独立描述”怎么跑起来”。备份、迁移、灾备演练的时候,你得记得凭据是另一条线,光备份 dotfiles 是不够的。

keychain 那条路不好版本化,也不好审计。 密钥在系统钥匙串里,它不像文件那样能 diff、能查改动历史。谁在什么时候换过这台机器上的 key,配置文件层面看不出来。要做密钥轮换的节奏管理,这条路给不了你可观测性,得靠上游(服务商控制台)那一侧的记录。

环境变量那条路,安全性取决于你怎么注入。 环境变量本身不是加密存储,它会被子进程继承,也可能被打进进程列表、崩溃转储、日志。把 key 直接明文写进 .bashrc 或者提交进仓库,等于绕开了整套设计——文件里没有,shell 配置里有,一样会泄露。

它不管的事,比它管的多。 这个机制解决的是”密钥不在项目配置文件里”这一件事。它不负责密钥轮换、不负责为不同项目分配不同权限的 key、不负责在密钥泄露后帮你止血、也不负责防止你把带 key 的请求日志打出去。这些属于更上层的治理问题,前面链的那三篇分别讲。

不适用的场景也存在。 如果你所在的环境本来就有一套集中的凭据注入体系(比如统一从密钥管理服务下发到容器环境),keychain 这条路反而是多余的一层;反过来,如果你就一台笔记本、不折腾容器,硬上环境变量只会让你多一个”忘了 export”的失败点。别为了看起来专业而选复杂的那条。

最后声明一句范围:本篇依据的那两页记录里没有出现”密钥在磁盘上以什么格式落地""是否支持从外部密钥管理服务直接拉取”这类信息,这不等于 Zed 没有相关能力,只是本篇不据此推断。以官方文档最新版为准。

六、避坑清单

坑一:以为 settings.json 里少了个字段,于是自己”补”一个 key 字段。 为什么会踩:其它几家客户端确实在配置里放密钥,你按肌肉记忆找那一行,找不到就以为示例省略了。怎么避:记住文档那句原话——不要把 API key 放进 settings.json。示例里没有就是真没有,别自创字段名,自创的字段客户端也不认。

坑二:改了 provider 名,忘了改环境变量名。 为什么会踩:环境变量名是从 provider 名推导的(my-providerMY_PROVIDER_API_KEY),这个耦合关系不写在同一行里,改名时容易只改一处。怎么避:把 provider 名当成”命名过的常量”,一旦定下来就别轻易改;真要改,把 settings.json 里的键和 shell 里的 export 一起改;改完新开一个终端,跑一句 echo $MY_PROVIDER_API_KEY(把变量名换成你推导出来的那个),打印出非空字符串才算这一步过了,再从这个终端启动编辑器。

坑三:api_url/v1 有没有,凭感觉填。 为什么会踩:各家 base URL 的形态不统一,同一个词在不同产品里指的东西也不同——Zed 这里叫 api_url,别家叫 apiBase、叫 OPENAI_HOST,接受的形态不一定一样,把别家的填法搬过来就是错。怎么避:只看两处——Zed 文档示例的形态(https://example.com/v1),和你的服务商文档里写的端点。两边对齐,不要跨产品套用。

坑四:available_models 里的 name 写成了 display_name 该写的东西。 为什么会踩:两个字段挨在一起,一个是给服务端看的模型标识,一个是给你看的显示名,写反了从配置本身看不出错。怎么避:按字段语义把方向记牢——name 对应模型标识,抄服务商文档里的模型 ID 时逐字比对,别自己改大小写或加后缀;display_name 是界面显示名。配好之后先发一句最短的话(比如”1+1”)验证,看得到正常回复才算这一步过了,别等到写长 prompt 时才发现连不上。以上为按字段名的判断,官方文档未逐条说明客户端如何使用这两个值。

坑五:把 key export 进 .bashrc 然后把 dotfiles 推上了公开仓库。 为什么会踩:你以为”没写进项目配置文件”就安全了,但秘密只是换了个文件躺着。怎么避:环境变量要么由外部系统注入,要么放在一个明确不进版本库的文件里;提交前扫一遍 dotfiles 仓库里有没有 _API_KEY= 这样的字面量。

坑六:以为配置一贴给同事就能跑。 为什么会踩:正是因为设计得干净,文件里看不出还缺什么。怎么避:团队里共享这份配置时,配套写一行说明——“密钥自己配,走设置界面或 <你的 provider 名>_API_KEY 环境变量”。这一行成本极低,能省掉大量重复答疑。

坑七:需要临时全关 AI 功能时,去一个个删配置。 为什么会踩:不知道有整体开关,只好手动拆配置,拆完还得拼回来。怎么避:文档给了 disable_ai 设置,写法是 "disable_ai": true。要交出屏幕、要在敏感仓库里干活时,用这个比拆配置稳妥。

数据来源与核对日期

本篇所有产品事实的核对日期为 2026-08-07,来源如下(均为各产品官方文档页面):

引用的英文原句(“Do not put API keys in settings.json.”、“Provider keys saved through Zed are stored in the system keychain, not in settings.json.”)均为 Zed 官方文档的说法,本文只做转述,不代表本文的独立断言。

本篇没有写的内容,以及原因:

  • 价格、免费额度、订阅档位、限速数字:这类数字变动频繁,本次未做核实,写出来会误导。以各产品官方文档与控制台为准。
  • 完整模型清单与版本号:同上,会过时。文中出现的模型相关字段只讲怎么填,不列具体可用模型。
  • 界面长什么样、菜单点哪几级、报错文案:本篇依据的文档记录的是配置层事实,不含界面细节,因此一律不描述。以你实际看到的界面为准。
  • 各家文档孰优孰劣:不做这种比较,只呈现设计取向的差异。
  • 本篇依据的页面里没出现的信息(例如密钥在磁盘上的具体落地形式),不等于产品没有该能力,只是本篇不据此推断,也不做绝对否定。

文中涉及具体字段与行为的部分,描述的是截至 2026-08-07 官方文档的情况;配置格式和字段名可能随版本变化,动手前请以官方文档最新版为准。

延伸阅读:同一组里的 Zed 接 OpenAI 兼容端点goose 怎么配模型 provider;接完之后照 接完自定义模型别急着干活 逐项过一遍,才算真接通。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。