1. 为什么我要在终端里常驻一个 AI 编码智能体
我平时 90% 的开发时间都待在终端里:改代码用编辑器,跑测试用命令行,提交代码用 git,排查线上问题靠 ssh 到服务器。前两年 AI 编程助手刚火起来的时候,我也跟风在 IDE 里装过各种插件,但用了一段时间之后发现一个问题:插件和你当前打开的项目是绑死的,换一个仓库、换一种语言,甚至换一台远程机器,它的上下文就全断了。更不用说在无图形界面的服务器上做部署任务或者应急排障,IDE 插件基本帮不上忙。
所以当终端原生的 AI 编码代理开始流行之后,我很快就切了过去。这类工具的本质,是把一个能读项目代码、能跨文件搜索、能改文件、在获得授权后还能执行命令的智能体,直接塞进你的终端工作流里。而我目前主力在用的开源方案是 OpenCode。它最打动我的不是界面多花哨,而是模型选择权完全开放:通过 Provider 机制,它可以对接任意提供标准对话接口的模型服务,不锁死在某几个官方品牌上。这意味着本地跑的推理服务、内部搭建的模型网关、以及各种第三方兼容平台,都可以接入同一个终端智能体里统一调度。
这篇内容不聊产品宣传话术,只讲一件最实在的事:如何把一个自定义模型 API 完整接入 OpenCode,并且用 3 步跑通第一次对话。同时我会把接入过程中真正会卡人的认证、路由、驱动包安装这些细节全部交代清楚。如果你准备接入本地模型、内部网关,或者任何走通用兼容协议的服务,这篇文章应该能帮你省掉不少排查时间。
2. 动手前需要搞清楚的几件事:安装、配置层级和三个核心概念
2.1 安装 OpenCode 与环境检查
先说安装。OpenCode 是 Node.js 生态里的工具,建议机器上提前准备好 Node 20 以上的运行环境。安装路径有两条:一是通过包管理器直接安装,适合已经统一了包管理工具的开发机;二是官方提供的一键脚本,适合临时机器和 CI 环境。两种方式没有本质区别,选一个你习惯的就行。
装完先别急着起服务,运行opencode --version确认版本号能正常输出。这一步如果就报错了,绝大多数情况是 Node.js 不在 PATH 里,或者全局安装目录没有被导出。先解决环境变量,再继续往下跑,不然后面排查问题时会多一层干扰。
2.2 配置文件的两级体系
OpenCode 的配置分为全局和项目两级。全局配置写在~/.config/opencode/opencode.json,项目级配置放在项目根目录的opencode.json或.opencode/opencode.json,后者会覆盖全局的同名项。这个设计对多项目团队非常实用:你可以把通用的 Provider 定义放在全局配置里,然后在具体仓库里只声明这个仓库默认使用哪个模型、允许 Agent 执行哪些命令。
项目级配置推荐提交到 Git 仓库里。我自己经历过最爽的场景是:新同事克隆仓库后什么都不用装,配置随仓库走,opencode拉起来就能用。这比每个人在本地重复贴配置文件省心太多了。
2.3 Provider、Model、Auth 三个概念各管什么
这是接入自定义模型前必须理解的三个抽象,边界搞清楚了,后面的配置就不会乱。
- Provider 管"怎么连"。它包含通信用的驱动包、服务地址 baseURL,以及该服务下可用的模型清单。
- Model 管"有哪些模型以及怎么展示"。一个 Provider 下面可以挂多个模型,每个模型可以有自己的显示名、用途标记。
- Auth 管"你是谁"。API Key 这类敏感凭证不写在 Provider 配置里,OpenCode 把认证单独放在 auth 层。
这个设计我把话说透一点:Provider 定义和模型清单是可以进版本库的共享信息,API Key 是绝对不能进版本库的私密信息,两者分开之后,你才能放心地把配置文件交给团队共享。理解了这一点,下面三步走起来就顺了。
3. 3 步跑通自定义模型:声明 Provider、注入 Key、验证对话链路
3.1 第 1 步:在 opencode.json 里声明 Provider 与模型清单
打开全局配置文件~/.config/opencode/opencode.json,写入一个自定义 Provider。现在主流的模型服务平台基本都提供通用的对话接口协议,也就是形如/v1/chat/completions的端点,所以接入时统一使用通用的@ai-sdk/openai-compatible驱动包即可。
{ "$schema": "https://opencode.ai/config.json", "provider": { "myprovider": { "npm": "@ai-sdk/openai-compatible", "name": "我的自定义服务", "options": { "baseURL": "https://api.example.com/v1" }, "models": { "my-model": { "name": "我的业务模型" } } } } }逐个字段说清楚。provider下面的第一层键myprovider是你给这个服务起的内部标识,后面默认模型指向和 auth 配置都靠它引用。npm字段告诉 OpenCode 用哪个驱动包建立连接,这是对接协议的关键,不能省。options.baseURL是服务地址,注意必须以/v1结尾,但不要再加尾斜杠,这一条后面我会专门讲为什么。models下面是模型清单,键名是请求时真正发送给后端的模型标识,name是界面上显示给用户看的友好名称。
这里有个很多人忽视的点:后端服务的模型名和界面显示名是可以解耦的。哪怕你的部署 ID 是一串晦涩的llm-prod-v3,你也可以在配置里给它配一个"我的业务模型"的显示名。OpenCode 界面上切换模型时看到的就是这个名字,日常使用会舒服很多。
3.2 第 2 步:用三种方式之一注入 API Key
自定义服务的密钥有三种配置方式,按使用场景选一个就行。
第一种最直接,运行opencode auth login,在交互界面里选择你刚声明的myprovider,然后粘贴 API Key。OpenCode 会把凭证写入认证文件,日常使用不用再管。第二种是手动编辑认证文件,添加同名字段:
{ "myprovider": { "api_key": "sk-..." } }第三种是走环境变量,在 Provider 配置里用占位符引用:
{ "provider": { "myprovider": { "npm": "@ai-sdk/openai-compatible", "name": "我的自定义服务", "options": { "baseURL": "https://api.example.com/v1", "apiKey": "{env:MY_PROVIDER_API_KEY}" } } } }三种方式怎么选,我的建议很明确:个人电脑、自己用的密钥,直接 auth login 最省事;项目要共享配置、多人协作,务必走环境变量,不要把明文密钥写进任何可能要提交的配置文件里。
3.3 第 3 步:启动 OpenCode,验证整条链路
配置写好之后,在项目目录里运行opencode启动 TUI。为了不让 Agent 在多个 Provider 之间随意挑模型,我习惯把默认模型写死:
{ "model": "myprovider/my-model" }格式是Provider标识/模型标识。这样一进会话,目标模型就已经是默认选项了。然后发一条简单的指令,比如让它读取项目 README 并总结项目结构。如果能正常返回内容,说明整条链路已经通了。
如果这一步没通,先别急着调 OpenCode,用 curl 直接打一次对话接口,确认后端服务本身是否健康:
curl http://localhost:1234/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"my-model","messages":[{"role":"user","content":"ping"}]}'curl 通了而 OpenCode 不通,问题基本出在认证或配置格式上;curl 本身都不通,那是后端服务或网络的问题,跟 OpenCode 无关。这个二分法能帮你把排查范围缩小一大半。
4. 实战变体:接本地服务和接内部网关的差别
4.1 本地服务:http、无鉴权、注意力放在安全上
很多人接自定义模型的第一站是本地推理服务,图的是数据不出机器、调试零成本。这类服务通常在本地监听一个端口,提供 /v1 兼容接口。接入方式和云端服务一模一样,唯一的区别是 baseURL 变成http://localhost:端口/v1,而且 API Key 常常是空的。
这里必须提醒一个安全细节:本地推理服务默认不鉴权,如果你开的端口监听了非回环地址,局域网里其他人可以直接调用你的模型服务。我见过有人在公司网络里这么跑,第二天被运维找上门。没有特殊需求,务必让服务只监听127.0.0.1,或者干脆加一层简单的 Key 校验。
4.2 内部网关:模型展示名和部署标识是两回事
走内部网关或者第三方聚合平台的时候,最容易踩的坑是模型标识不一致。很多平台界面上写的是"旗舰对话模型"这样的友好名称,但真正的接口模型 ID 可能是llm-prod-v3这种部署标识。你在 OpenCode 配置里如果用展示名去填 models 的键名,界面里能选到模型,一发请求后端就直接报模型不存在。
所以接网关之前,先去平台文档里找到接口层级的 model 参数,把这个真实标识填进配置。这一条建议值回票价,我身边至少三个人在这个问题上卡了半小时以上。
4.3 大小模型分工:让轻量模型干杂活
OpenCode 内部有不少轻量任务,比如给会话生成标题、总结历史上下文。这类任务不需要动用大模型,你完全可以把更便宜的小模型配成 small 模型:
{ "provider": { "myprovider": { "npm": "@ai-sdk/openai-compatible", "name": "我的自定义服务", "options": { "baseURL": "https://api.example.com/v1" }, "models": { "my-model": { "name": "我的业务模型" }, "my-small": { "name": "轻量模型", "small": true } } } } }加了"small": true之后,标题生成、上下文摘要这类辅助任务会优先走小模型,主对话继续用默认大模型。这个配置对控制成本非常有效。我实测跑了一整天,几百次会话下来,大模型的调用量明显降了一截,而体验几乎没有感知差异。
5. 接入过程中的高频故障:我自己踩过的四个坑
5.1 现象:401 认证失败,但 Key 明明是对的
这个坑最常见的成因不是 Key 错了,而是 Key 没有正确送到 OpenCode。如果你用的是环境变量方式,先检查占位符里的变量名和 shell 里导出的变量名是否完全一致,再确认这个环境变量是在启动opencode的同一个 shell 会话里导出的。很多人把变量写进了.zshrc,重启终端却忘了source,直接起服务自然就 401。
5.2 现象:baseURL 多一个尾斜杠导致 404
很多服务 SDK 会在 baseURL 后面拼接/chat/completions,如果你把 baseURL 写成带/v1/尾斜杠,最终请求可能变成https://api.example.com/v1//chat/completions,后端的路由直接 404。这个问题极其隐蔽,因为配置文件看起来没毛病,但请求路径就是多了一个斜杠。我的习惯是 baseURL 一律不带尾斜杠,能避开一大批怪问题。
5.3 现象:首次使用某个 Provider 时卡在包安装
OpenCode 会根据 Provider 配置里的npm字段,在首次使用时拉取对应驱动包。在某些网络环境下,这个过程偶尔会因为包源缓慢而超时。解决办法是提前把 npm 的 registry 换成可用的镜像源,然后手动安装@ai-sdk/openai-compatible,装好之后 OpenCode 能直接复用本地包,不再重复拉取。
5.4 现象:模型能选,但一对话就报错
如果界面上模型列表正常、发起对话却立刻失败,优先确认模型标识与后端严格一致。另一个容易忽略的点是:部分兼容服务只实现了基础对话协议,没有完整实现工具调用所需的接口,Agent 一调用扩展能力就崩。这种时候回到 curl,用带tools参数的请求体逐一验证服务能力,就能区分是服务能力不足还是配置问题。
5.5 兜底手段:打开调试日志
如果上面几招都定位不到,直接打开调试输出。OpenCode 的日志等级可以调整,重启之后观察请求打到了哪个 URL、请求头里携带的 Key 是否正确,基本都能找到线索。我整理了一份常见现象的对照表,排查的时候可以直接对照:
| 现象 | 常见根因 | 快速验证方式 | 解决办法 |
|---|---|---|---|
| 401 认证失败 | 环境变量未生效或 Key 错误 | 检查 shell 导出与占位符 | 重新导出变量或走 auth login |
| 404 路径错误 | baseURL 尾斜杠多余 | 看日志请求 URL | baseURL 去掉尾斜杠 |
| 包安装失败 | 网络源不可用 | 手动执行包管理器安装 | 换镜像源后预装驱动包 |
| 模型不可用 | 标识与后端不一致 | curl 验证 model 参数 | 改为部署真实标识 |
| 对话立即失败 | 服务缺少工具调用能力 | curl 带 tools 参数测试 | 换完整服务或关闭相关能力 |
6. 跑通之后,我在日常使用里沉淀的几个习惯
接好自定义模型只是开始,真正提升体验的是后续几个使用习惯。
第一,在项目根目录维护一个AGENTS.md文件。把项目的技术栈、目录结构、常用命令都写进去,Agent 每次进入会话都会参考这份文件,相当于给智能体一份"项目入职手册"。我用下来最明显的感觉是,有了这个文件之后,Agent 不再反复问"项目的启动命令是什么"这类基础问题。
第二,认真对待权限确认机制。Agent 执行命令前会询问,确认过的规则可以记住。但我的建议是,对删除类操作、涉及敏感目录的命令,把确认机制留得严一点。终端智能体是一把好用的刀,但也得有刀鞘。
第三,多会话管理用起来。一个会话对应一个任务,任务结束及时归档,不要让上下文无限累积。上下文越拖越长,推理质量和响应速度都会下降,这跟人脑开会开太久会疲劳是一个道理。
第四,接多家服务之后,在 TUI 里切换模型只需要一条指令,不用改配置文件。这个特性在做模型对比测试时特别顺手:同一个问题分别丢给不同模型,看回答差异,几分钟就能出一个初步结论。
最后分享一点个人体会。OpenCode 真正值钱的地方,不是某个花哨功能,而是把模型选择权完全交到了用户手里。官方托管模型固然省事,但自定义接入带来的成本控制、数据边界和模型自由度,才是终端原生编码代理的核心竞争力。如果你正准备把本地模型或内部网关接进来,按上面三步走,再加一点耐心处理认证和标识问题,一个小时内跑通基本没有问题。