开源终端 Agent opencode 的两条模型接入路线怎么选

2026-08-04

本文基于 opencode 仓库 commit 7fe9938(2026-08-04)梳理,该项目仍在高频迭代,具体行为以仓库 https://github.com/anomalyco/opencode 最新代码与文档为准。

opencode 自带的那条模型接入线不是它的运行前提,而是一个可选渠道;你真正在决定的,是把「哪个模型配哪个服务端才跑得好」这件验证工作外包出去,还是自己扛下来。 这一句是仓库文档里反复写明的立场,而不是我的推断——packages/web/src/content/docs/zen.mdx 里加粗写着 completely optional,providers.mdx 里介绍这条线的那一节末尾也重复了一遍「It works like any other provider in OpenCode and is completely optional to use」。

先做个消歧:这篇讲的 opencode 是那个跑在终端里的开源编码 Agent 项目(MIT 许可证,LICENSE 里写着 Copyright 2025 opencode),不是泛指的开源代码,也不是任何一个名字相近的模型。它自带的那条接入线在文档里叫 OpenCode Zen,是同一个团队做的模型网关,跟项目本体是两个东西。

站内已经有几篇讲通用形态的文章:聚合中转 API 与官方直连的对比 讲的是中转商这一类服务本身的取舍,几种 API 接入方式的对比 讲的是接入形态的分类,模型路由策略 讲的是多模型之间怎么分流。这一篇不重复那些通用结论,只做一件事:把 opencode 这一个具体项目里两条路的机制落到文件和代码行上,让你能自己打开仓库核对。

一、这条自带的接入线在解决什么问题

zen.mdx 的 Background 一节把动机说得很直白:模型很多,但真正能当编码 Agent 用的只有少数;而且同一个模型在不同服务商那里配置方式差异很大,跑出来的性能和质量也不一样。文档举的例子是,如果你通过某个聚合网关调用模型,你没法确定拿到的是不是这个模型该有的那个版本。

针对这个问题,文档列了三步做法:测一批模型,并且和这些模型的团队沟通怎么跑才对;再和几家服务商配合,确保模型被正确地服务出来;最后对「模型 + 服务商」这个组合做基准测试,得出一份他们自己愿意推荐的清单。OpenCode Zen 就是让你访问这批模型的网关。

从使用者视角看,它和别的服务商没有任何形态差异。文档给的步骤只有三步:在 TUI 里跑 /connect 命令,选中 OpenCode Zen,把 API key 粘进去;然后跑 /models 看清单。同一份 providers.mdx 里还写了另一个叫 OpenCode Go 的订阅计划,接入入口同样是 /connect 里选一项,随后到浏览器完成授权。

这里有个容易被忽略的点:接入方式的一致,意味着它在架构上没有特权。 它在配置里就是一个普通的 provider id,模型 id 写成 opencode/<model-id> 这种 provider_id/model_id 格式,和你写 anthropic/... 或者自定义 provider 的写法完全同构。文档 Goals 一节把这件事挑明了:no lock-in,你可以拿它去配别的编码 Agent,也可以在 opencode 里用任何别的服务商。

二、自己填 key 那条路,链路上都有谁

models.mdxproviders.mdx 开头都写了同一句话:opencode 靠 AI SDK 加 Models.dev 这份模型目录来支持一大批 LLM 服务商,并且支持跑本地模型。这条路上有四个环节值得单独拎出来看。

凭据存哪。 /connect 存下来的凭据落在 ~/.local/share/opencode/auth.json。翻 packages/opencode/src/auth/index.ts 能看到这个文件里的条目分三种形态:oauth(带 refresh、access、expires)、api(就是一个 key 字段)、wellknown。也就是说不是所有服务商都靠一串 key——文档里 Anthropic 和 OpenAI 那两节,除了「手动粘 API key」还各给了一条浏览器登录的订阅通道,GitHub Copilot 走的是设备码(/connect 之后到 GitHub 的设备验证页填一串短码),xAI 有浏览器 OAuth 和无浏览器场景的设备码两种,DigitalOcean 和 Snowflake Cortex 也各有 OAuth 路径,Amazon Bedrock 干脆走 AWS 自己的凭据链。排查的时候可以跑 opencode auth list 看凭据在不在,文档的 Troubleshooting 一节特意提醒:像 Bedrock 这种靠环境变量的不适用这条。

模型元数据从哪来。 这块在 packages/core/src/models-dev.ts。目录不是编译进二进制的静态表,而是从一个远端拉下来缓存到本地:

const source = Flag.OPENCODE_MODELS_URL || "https://models.opencode.ai"

拉到的 api.json 写进本地缓存目录,进程里会 fork 一个后台任务按 60 分钟的间隔刷新。三个环境变量可以改这套行为:OPENCODE_MODELS_URL 换源,OPENCODE_MODELS_PATH 直接指向一份本地文件,OPENCODE_DISABLE_MODELS_FETCH 把远端抓取整个关掉。离线机器和内网环境要靠这三个才能把模型列表喂进去。

配置层能改什么。 providers.mdx 里几个键都值得记住:options.baseURL 换端点(代理、私有部署、VPC 端点都靠它);blacklist 把不想看到的模型从 /models 选择器里去掉,whitelist 反过来只留列出的那些,两者可以叠加,先由 whitelist 收窄再由 blacklist 剔除。任何 OpenAI 兼容的服务商都能自己声明成一个 provider:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "myprovider": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "My AI ProviderDisplay Name",
      "options": {
        "baseURL": "https://api.myprovider.com/v1"
      },
      "models": {
        "my-model-name": {
          "name": "My Model Display Name"
        }
      }
    }
  }
}

本地模型走的也是这一套:文档里 llama.cpp、LM Studio、Ollama、Atomic Chat 的例子结构完全一样,区别只在 baseURL 指向本机端口。

参数怎么调。 models.mdx 里的 variants 机制是这条路上真正要花时间的地方——同一个模型可以定义多个变体,各自带不同的推理档位或思考预算,用 variant_cycle 这个快捷键在运行时切换。项目内置了一批默认变体,Anthropic 侧是 highmax,OpenAI 侧大致是 nonexhigh 的一串档位,Google 侧是 lowhigh。你自己填 key 的时候,这些档位怎么配、配到哪一档才划算,是你自己的活。

三、两条路各自把什么交给了谁

组成部分它负责什么对应仓库位置你什么时候会碰到它
自带模型清单与网关挑模型、和服务商对齐部署方式、跑基准、给出一份推荐清单packages/web/src/content/docs/zen.mdx想省掉选型验证、一个 key 通吃多家的时候
服务商目录与自填凭据列出各家的接入步骤、认证形态、配置示例packages/web/src/content/docs/providers.mdx公司已有账号、要走自建网关或本地模型的时候
凭据存储把 oauth / api / wellknown 三类凭据落盘packages/opencode/src/auth/index.ts排查「为什么没认上」、做机器迁移与密钥轮换的时候
模型元数据目录拉取并缓存模型清单与能力字段,定期刷新packages/core/src/models-dev.ts内网离线、要换源或钉死一份清单的时候
服务商加载与特判决定哪些 provider 自动加载、没凭据时怎么降级packages/opencode/src/provider/provider.ts装完发现模型列表和文档对不上的时候
模型选择与变体默认模型、变体档位、启动时的模型优先级packages/web/src/content/docs/models.mdx想固定默认模型、想按任务切推理档的时候
权限闸门决定哪些动作自动跑、哪些要你点头、哪些直接拦packages/web/src/content/docs/permissions.mdx任何一条路配好之后,动手跑第一个任务之前

四、没连凭据的时候,你看到的清单是被裁过的

这一节是翻源码才看得到、光看文档看不出来的。packages/opencode/src/provider/provider.ts 里给自带 provider 单独写了一个分支:先看环境变量里有没有对应的键,再看 auth.json 里有没有条目,再看配置里 provider.opencode.options.apiKey 有没有值。三处都没有的话:

if (!ok) {
  for (const [key, value] of Object.entries(input.models)) {
    if (value.cost.input === 0) continue
    delete input.models[key]
  }
}

return {
  autoload: Object.keys(input.models).length > 0,
  options: ok ? {} : { apiKey: "public" },
}

输入成本不为零的模型会被从清单里删掉,只留下成本为零的那些,同时把 apiKey 设成 public。效果是:装完不做任何配置也能直接开跑,但你看到的是一份被裁剪过的清单——这不是 bug,也不是缓存没刷新。

对你意味着两件事。第一,如果你发现 /models 里的选项比文档少一大截,先确认凭据认上了没有,而不是去怀疑模型下线了。第二,也是更要紧的一条:zen.mdx 的 Privacy 一节写明,多数模型走的是零留存策略,但处于免费阶段的那几个是例外,收集到的数据可能被用于改进模型,其中还有两条额外标注了「不要提交个人或机密数据」,并且各自指向了对应厂商自己的条款与隐私政策链接。你在毫无配置的状态下顺手跑起来的那次会话,用的很可能就是这一档。

还有一个默认行为同样值得注意。config.mdx 里的 small_model 用来指定处理轻量任务(比如生成会话标题)的模型,默认情况下 opencode 会尽量挑一个更便宜的小模型。provider.ts 里能看到自带 provider 的小模型家族优先级被单独指定了。providers.mdx 的 GitLab 自托管那一节把这件事的合规含义直接写了出来:默认那个小模型是由 Zen 托管的,要想把 opencode 锁死在自家实例上,需要显式配置:

{
  "$schema": "https://opencode.ai/config.json",
  "small_model": "gitlab/duo-chat-haiku-4-5",
  "share": "disabled"
}

换句话说,就算你主模型全部走自己的 key,标题生成这类边角调用仍可能走出你以为的边界。这类凭据与出口边界的一般性讨论可以看 API key 的安全管理,这里只强调一点:边界要按调用逐个确认,不能按主模型一刀切地推断。

五、边界与代价

自带那条线放弃了什么。 多了一跳网关,链路上多一个需要信任的环节。zen.mdx 写明所有模型托管在美国;零留存政策带有例外,除了免费期模型,走 OpenAI 与 Anthropic API 的请求按各自的数据政策保留一段时间。模型可用性受这份清单管辖——文档里有一张明确的下线表,模型是会被移除的;团队工作区里管理员还能禁用特定模型,请求被禁用的模型会直接返回错误。这些规则各家不同且会调整,以官方最新说明为准。

自己填 key 那条路放弃了什么。 选型和调参全归你。哪些模型的工具调用真的靠谱,models.mdx 只给了一份明确标注为「既不穷举也不保证是最新」的列表;推理档位、思考预算怎么配没有标准答案。出问题的时候,你要自己分辨是模型本身不行、是这家服务商的部署方式有问题、还是 opencode 这边的配置错了——而这恰好就是自带清单那条线试图替你消化掉的成本。

两条路都不管的事。 这是个会在你机器上跑 shell 命令、直接改你代码文件、把代码内容发给模型服务商的工具,packages/opencode/src/tool/ 下的 edit.tswrite.tsshell.ts 就是干这个的。选哪条接入线,跟这三件事的风险毫无关系:模型接入线不会替你兜住误删误改,不会替你判断哪段代码不该发出去,也不会替你收窄权限。这部分归 permissions.mdx 那套配置管,规则只有三种取值——allow 直接跑、ask 问你、deny 拦掉,并且支持按工具输入做细粒度匹配。命令行上的 --auto 会把所有不是显式 deny 的请求自动放行,显式 deny 仍然生效。这块的思路可以参考 Agent 权限开太大会发生什么

明确不适用的场景。 代码不允许出境、不允许经过第三方网关的团队,自带那条线从一开始就不该进候选;这类情况要走自建网关(改 baseURL)或者本地模型。反过来,如果你只是想尽快跑起来、不想为「这个模型在这家跑得对不对」做验证,自己一家家去申请 key 反而是在给自己加活。

还有一条边界是文档单独强调的:有些插件能让你把 Claude 的订阅接进 opencode,providers.mdx 里明写 Anthropic 禁止这种用法,项目也已不再捆绑相关插件;同一段还列出了几家允许在 opencode 里直接使用的订阅。这类条款属于服务商侧的规则,会变,用之前自己去确认当前口径。

六、上手与避坑清单

  1. 别把首次打开看到的清单当全集。 会踩是因为没凭据时非零成本的模型会被代码删掉,界面上不会有任何提示。跑 /connect 接一条线,再用 opencode auth list 确认凭据落盘了,然后才去比对 /models 的结果。
  2. 模型 id 别只写模型名。 会踩是因为格式是 provider_id/model_id,两段都不能省。自带那条线的前缀是 opencode/;自定义 provider 的 provider_id 是你配置里 provider 下面那个 key,model_idprovider.models 下面的 key,两处对不上就找不到模型。
  3. 合规敏感就显式配 small_model 会踩是因为它有默认值,而默认值可能不在你以为的边界内。按 GitLab 自托管那节的写法,把 small_model 指到你自己的实例,同时考虑把 share 设成 disabled
  4. 自定义 provider 选对 npm 包。 会踩是因为两个包对应两种端点形态:/v1/chat/completions@ai-sdk/openai-compatible/v1/responses@ai-sdk/openai,选错了直接报错。文档 Troubleshooting 一节的第二条(「检查自定义 provider 的配置」)就在讲这个,同一条还提醒 provider id 要和 /connect 里用的那个对上;混合场景可以按模型覆盖 provider.npm
  5. 自定义 provider 记得写 limit 会踩是因为标准 provider 的上下文与输出上限是从模型目录自动带出来的,自定义的带不出来,opencode 就算不出你还剩多少上下文。在 models.<id>.limit 里补上 contextoutput 两个字段。
  6. 内网机器先解决模型目录。 会踩是因为目录默认要联网拉取,拉不到的时候模型列表是空的,而报错未必落在你正在看的地方。用 OPENCODE_MODELS_PATH 指一份本地文件,或者用 OPENCODE_MODELS_URL 换成内网镜像,必要时用 OPENCODE_DISABLE_MODELS_FETCH 关掉抓取。
  7. 权限先收紧再开跑。 会踩是因为这工具默认就有改文件和跑命令的能力,而 --auto 会把非 deny 的请求全部放行。先用对象语法把破坏性命令 deny 掉,确认行为符合预期之后再逐条放宽。
  8. 别把某个具体模型 id 钉死在没人看的地方。 会踩是因为模型会下线,zen.mdx 里那张下线表就是证据。CI 脚本或团队共享配置里写死一个 id,某天就会静默失败。

收尾

判断这两条路,其实只需要问自己三个问题:我的代码允不允许经过第三方网关;「这个模型配这个服务端跑得对不对」这件验证工作,我有没有人有时间去做;出问题的时候,我需不需要一条能自己端到端排查的链路。三个答案凑齐,选哪条基本就定了——而且这两条并不互斥:凭据是按 provider 分别存的,接了一条不妨碍再接另一条;zen.mdx 的 Bring your own key 一节还写明,可以在用自带清单的同时带上自己那家的 key,那部分用量由那家直接结算。

真要动手的话,读文件的顺序建议是:先 packages/web/src/content/docs/providers.mdx 找到你那家的接入步骤,再 models.mdx 看模型 id 格式和变体机制,然后 permissions.mdx 把权限收好。行为和文档对不上的时候,去翻 packages/opencode/src/provider/provider.ts——那里面的特判分支,往往就是你困惑的来源。

本篇属于一个把开源AI 编程 Agent 项目 opencode逐层拆开讲的系列,整体地图见 opencode 是什么:终端里的开源 AI 编程 Agent 全景地图;沿着这条线往下,还可以看 opencode 怎么接模型:终端编码 Agent 的供应商层与认证机制开源终端 Agent opencode 的配置从哪读,改错一处为何全变

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