OpenCode 开源编码代理:从模型接入到终端权限实践 1. 先把 opencode 是什么说清楚再谈怎么用opencode 使用这件事我最初是被同事在群里甩了一张终端截图吸引过去的左边是文件树右边是对话流中间一行输入框改完代码直接在同一个界面里跑测试整个过程没离开过终端。这类工具这两年冒出来不少但大多数要么绑死在某一家模型服务上要么必须开个重型 IDE 才能用。opencode 的定位很明确——一个跑在终端里的开源编码代理coding agent模型可以自己接界面是 TUI行为可配置、可扩展、可脚本化。它本身不产出智能它做的是把「模型 你的仓库 一堆本地工具」粘在一起的那层胶水。先说清楚它能干什么省得你抱着错误预期去折腾。日常最典型的三个用法一是让它读一批文件后回答「这个鉴权流程是怎么串起来的」比你自己翻 grep 快二是给它一个明确的改动任务它自己找文件、自己改、自己跑 lint 和测试改坏了能回滚三是把它当成一个可编程的壳用自定义命令、规则文件、外部工具接口把它调成贴合你自己仓库习惯的样子。它解决的问题不是「帮你写一段函数」而是「把一次跨文件的、需要反复验证的改动压缩成一句话 几次确认」。适合谁来参考这篇手里有终端使用习惯、仓库有一定规模、被跨文件重构和排查折磨过的开发者收益最大。完全没碰过命令行的同学会有点吃力因为它的所有能力都建立在「你愿意敲命令」这个前提上。另外如果你是那种希望开箱即用、不想碰配置文件的类型那它前半小时的配置成本会让你烦躁——这点我后面会详细说怎么把这段时间压到最短。还有一点必须先摆明opencode 是开源项目迭代很快配置项的字段名和默认行为在不同版本之间会有出入。我这篇里给的所有配置片段都是「结构正确、字段含义稳定」的写法具体到某个小版本上可能要微调一两个键名遇到报错先去看它的 schema 文件和--help比在搜索引擎里翻旧帖子靠谱得多。下面所有内容都是基于我自己的实际使用习惯和常见实践补全的不是官方文档翻译。2. 装起来、跑起来从零到第一次对话2.1 三种安装方式的取舍别一上来就选最麻烦的安装方式大致三条路官方安装脚本、包管理器、从源码构建。我的建议很直接——先用安装脚本跑通确认能用了再考虑换别的因为你还没体会到它的价值就先花二十分钟处理依赖冲突热情会被磨光。安装脚本通常长这样地址到项目主页找最新的curl -fsSL 安装脚本地址 | bash这条命令的本质是下载一个编译好的二进制丢到你的 PATH 里不依赖 Node 运行时启动快升级也简单。缺点是它对系统架构有要求比较冷门的架构上可能没有现成产物。包管理器路线适合已经在用 Node 生态的人# 用你习惯的包管理器全局安装 npm i -g opencode-ai # 或 bun add -g opencode-ai这条路的好处是升级跟着包管理器走版本管理清晰坏处是全局安装的包在某些系统上会被权限策略拦住尤其是用系统级 Node 的环境可能需要配一下全局前缀。第三条是从源码构建只有在你想改它、想给上游提改动、或者想验证某个未发布行为时才值得走。我试过一次坑主要在依赖体积和构建产物路径上纯粹为了「用」的话完全没必要。方式上手成本升级方式适合谁安装脚本低重跑脚本或内置升级绝大多数人尤其是不想管运行时版本包管理器中跟着包管理器已经重度使用 Node 工具链的人源码构建高自己拉代码重建想改源码、想验证未发布特性提示不管走哪条路装完先执行一次opencode --version确认二进制确实在 PATH 里。我见过好几次「装完了但敲命令提示找不到」的情况本质是 shell 的 PATH 没刷新重开一个终端窗口就好了别急着去重装。2.2 第一次启动会经历什么第一次在某个目录下敲opencode它会做几件事启动一个 TUI 界面、读取全局配置和当前项目配置、扫描当前目录结构。这时候你直接输入一句话它会告诉你还没有可用的模型——这是新手最容易卡住的地方很多人以为装完就能用其实装完只是个空壳模型接入是必须手动完成的一步。界面上有几个东西值得先记一下Tab 切换 agent。默认有 build 和 plan 两个前者可以改文件、执行命令后者只读不写专门用来做方案讨论。这个区分很重要后面单独讲。/开头是斜杠命令比如/init、/models、/compact、/undo、/share。敲/会弹出补全列表不用背。开头是文件引用输入之后会做模糊搜索选中哪个文件就会把那部分内容带进上下文。!开头可以执行本地 shell 命令输出直接进对话上下文。这个在「跑一下测试看看报什么错」的场景里极其顺手比让模型自己去猜命令快得多。我把!这个功能单独拎出来说是因为它改变了我用这类工具的姿势。以前我是「描述问题 → 等模型猜 → 它猜错了再纠正」现在是「先把真实报错贴进去 → 它基于真实信息判断」。信息质量差一个量级结果差一个量级。2.3 配置文件放哪儿优先级怎么算这是必须搞清楚的一件事否则你会遇到「我明明改了配置怎么不生效」。惯例是两层全局配置放在用户配置目录下通常是~/.config/opencode/opencode.json影响你所有的项目。项目配置放在仓库根目录的opencode.json只对这个仓库生效。项目配置会覆盖全局配置里的同名字段。我自己的习惯是全局只放模型凭证和通用偏好项目里放这个仓库特有的规则、命令、外部工具。这样换项目不用重复配而项目特有的东西跟着代码走团队其他人 clone 下来就能用——这一点比很多同类工具的「配置全塞在用户目录」的设计要合理得多也是我最终留下来用它的重要原因。另外还有一个AGENTS.md跟配置文件是两回事配置文件管的是「工具怎么跑」AGENTS.md管的是「模型该知道什么」。前者是程序读的后者是模型读的别混。3. 模型接入这一步配不好后面全是白搭3.1 一段完整配置拆开看配置的核心是provider这一段。假设你要接一个兼容主流接口协议的服务端点写法大致是这样{ $schema: 项目的配置 schema 地址, model: myprovider/big-model, provider: { myprovider: { npm: 兼容适配层的包名, name: 自建端点, options: { baseURL: https://api.example.com/v1, apiKey: {env:MY_PROVIDER_KEY} }, models: { big-model: { name: 大杯, limit: { context: 200000, output: 32000 } }, small-model: { name: 小杯, limit: { context: 128000, output: 8000 } } } } } }几个关键点我逐个说为什么要这么写provider下的键名上面是myprovider是你自己起的别名只在配置文件里用model字段里写别名/模型名就能选到。取个有辨识度的名字别用test、a这种回头自己都看不懂。npm字段指的是适配层的包名——因为不同服务商的接口协议细节有差异需要一层薄薄的转换。绝大多数兼容主流协议的服务用社区现成的兼容适配包就行如果你接的服务比较特殊可能要用它专门的适配包。这个字段填错的表现通常是启动时直接报模块解析失败很好定位。apiKey我用的是{env:变量名}的写法而不是把明文密钥写进文件。原因很实在配置文件很容易被顺手提交进仓库一旦提交了密钥就等于公开了。用环境变量引用文件随便传密钥留在本地。如果你的环境不方便设环境变量也可以用工具自带的凭证管理命令把密钥存到独立的凭证文件里那个文件默认不会跟项目走。limit这一段是最容易被忽略但影响最大的参数。context是模型能吃下的上下文长度output是单次回复的输出上限。这两个值如果填得比模型真实能力大会出现一种很难查的现象对话跑到一半突然报错或者输出被硬生生截断。如果填得比真实能力小又白白浪费能力。最稳的做法是查服务方文档里的准确数字然后往下取个整比如文档写 200K你填 180K 左右留点余量。3.2 上下文长度这个参数值不值得纠结值得而且比你想象的值得。我踩过的坑是这样的一开始我把context随手填了个很大的数字结果在处理一个两百多个文件的仓库时它读文件读到某个点突然全线报错我以为是工具 bug折腾了半天才反应过来是超了真实上限。反过来的坑更隐蔽填太小的时候它在处理稍大的任务时会在中途做上下文压缩压缩之后它就「忘」了前面看过的文件内容开始重复读同样的文件或者基于错误的记忆改代码。表现是「越改越偏」你还以为是模型能力不行。我的经验值是这样日常提问和中等规模改动128K 这个档位基本够用做跨模块重构、需要一次性读进十几个文件的场景才需要往上加。而且不是越大越好上下文越长单次请求成本和延迟都涨某些模型在超长上下文里的注意力也会散。合理做法是给自己配两档模型——大杯用于硬任务小杯用于日常零碎用/models随时切。3.3 多模型分工是我用过最划算的一招配置里可以定义多个模型我一般至少配两个用途模型档位选择逻辑读代码、找调用链、写注释、改小 bug小杯快、便宜这类任务对推理深度要求不高但对响应速度敏感跨文件重构、设计方案、排查复杂问题大杯慢、贵需要长上下文和更强的推理链条plan 模式下的方案讨论大杯方案错了后面全错这里不能省切换方式就是在输入框敲/models选或者用命令行的--model参数临时指定。这个分工看起来是多此一举实际用下来成本能差好几倍——你不会想用最贵的模型去问「这个函数在哪儿定义的」。注意不同模型对「工具调用」的支持程度不一样。有些模型能稳定地按格式输出「我要读某个文件」这样的结构化指令有些则会把它写成一段自然语言描述导致工具链解析失败。如果你的模型接入后在需要读文件的场景里总是「说而不做」八成是工具调用支持不到位换个模型试比改配置快。4. 日常用法从一句话到一整套流程4.1 plan 和 build 这两个模式别用混了这是 opencode 用起来最舒服的设计之一。plan 模式只读它能读文件、能搜代码、能给你分析但不会动你的代码build 模式可以写文件、可以执行命令。两者用 Tab 切换。为什么这个区分重要因为编码代理最大的风险不是「写得不好」是「在错误的理解上大刀阔斧地改」。我现在的固定流程是在 plan 模式下把需求讲清楚让它先读相关文件输出一份改动方案包括要改哪几个文件、每个文件改什么、有什么风险。我审一遍方案不对的地方直接在 plan 模式里纠正让它重新出方案。方案认可了切到 build 模式把方案作为上下文让它执行。这个流程看着多了一步实际省时间。我早年用别的工具时习惯一句话丢过去让它直接改结果是它改完我得逐行 review经常发现方向就错了等于白改一轮还要回滚。花两分钟在只读模式里对齐理解比花二十分钟 review 一堆错改要划算得多。4.2 会话、回滚和上下文压缩会话session是它的基本单位。每次启动默认新开一个也可以接着上次的继续。几个我高频使用的操作/undo回滚上一轮。它同时回滚对话记录和文件改动这一点非常关键——很多工具只能回滚对话代码改动留在工作区里你得自己去 git 里捞。/redo反悔了再放回来。/compact手动触发上下文压缩。当对话很长、你感觉它开始「记不清」前面的事时主动压一次比等它自动压缩更可控。/share把当前会话生成一个可访问的链接。关于/share我要单独提醒一句它会把你当前的对话内容包括被读进上下文的代码片段发布出去。在自己电脑上随手用没问题但如果你在一个有保密要求的仓库里工作这个操作的风险很高用之前先想清楚上下文里有没有不该外传的东西。我的习惯是把它当成一个默认关闭的功能只在需要跟同事复现某个问题时才用。会话管理的另一个实用点是命令行参数。想接着上一次的会话继续可以直接用续接参数启动想指定某个历史会话用会话 id 指定。这个在「昨天做到一半今天接着来」的场景里很省事不用重新交代背景。4.3 AGENTS.md 和自定义命令把它调教成你团队的样子/init这个命令值得在任何一个正式项目里跑一次。它会扫描你的仓库结构生成一份AGENTS.md里面大致包含项目用什么语言、目录怎么组织、怎么跑测试、有哪些约定。这份文件会在每次对话时被自动读进去相当于给模型的一份「新人入职手册」。自动生成的版本只能算个起点真正有价值的是你手动往里补的那几段。我一般会补这些哪些目录是自动生成的、绝对不能手改这个能省掉大量无意义的改动。提交信息的格式约定、分支命名规则。项目里那些「看起来像 bug 其实是故意这么写的」地方比如某个绕了一圈的兼容逻辑。本地开发环境的启动方式特别是需要特定环境变量或者本地服务的那种。自定义命令是另一层。在项目里建一个命令目录放 markdown 文件文件名就是命令名。文件开头用 frontmatter 声明描述、用哪个 agent、用哪个模型正文就是提示词模板。举个例子我常备一个「提交前自查」的命令内容大意是「检查当前工作区的改动按以下清单逐条核对有没有调试代码残留、有没有硬编码的本地路径、新增的公开函数有没有注释、改动是否影响到了调用方」。敲一个短命令就能跑一遍比自己回忆清单靠谱。--- description: 提交前自查改动 agent: plan --- 检查当前工作区的全部改动逐条核对 1. 是否有 console.log / 调试打印残留 2. 是否有硬编码的本地绝对路径 3. 新增的导出函数是否有注释 4. 改动的函数签名是否影响调用方 输出问题清单不要修改代码。注意这里我把 agent 设成了plan——自查这种事不该让模型顺手改代码只出报告就够了。用哪个 agent 是个安全边界问题不是风格问题。5. 让它真正读懂你的仓库LSP、外部工具与插件5.1 接上语言服务之后定位精度完全不是一个级别纯靠文本搜索找符号问题是显而易见的同名函数一堆、动态引用找不到、重命名之后旧的引用还留在搜索结果里。opencode 支持接入语言服务LSP接上之后它能拿到真正的符号定义、引用位置、类型信息。实际差别有多大我拿一个中等规模的类型化项目试过让它找「某个接口的所有实现类」。没接 LSP 时它是靠字符串匹配猜的漏了两个、多报了一个接上之后一次就准了。在重构类任务上这个差距直接决定了你敢不敢让它动手。配置上一般就是声明用哪种语言、启动命令是什么工具会在需要时把语言服务拉起来。要注意的是语言服务本身有启动成本第一次查询会慢几秒之后就快了。如果你在一个超大仓库里感觉卡先看看是不是某个语言服务在反复重启。5.2 外部工具接不接取决于你信不信它外部工具业界一般叫 MCP这块我的态度是克制。接一个工具就等于给它开一个能力口子接了数据库工具它就能查你的库接了工单工具它就能读你的需求单。能力越强出错时的破坏面越大。我的做法是分层工具类型接不接理由文档检索类只读接风险低收益直接能让它查内部规范代码搜索类只读接补足本地搜索的短板数据库只读账号谨慎接必须用只读账号且要能审计任何带写操作的默认不接除非有明确的一次性需求用完就关配置上通常是在配置文件的工具段里声明类型本地进程还是远程服务、启动命令、环境变量、是否启用。能用本地进程就别用远程服务本地进程的权限边界更清楚出问题也好断。5.3 权限配置这是整个工具里最该花时间的一块这一块我要重点说因为它直接决定「你敢不敢让它自己跑」。权限配置可以细到「哪个命令允许、哪个要问、哪个直接拒绝」{ permission: { edit: ask, bash: { *: ask, git status: allow, git diff*: allow, ls *: allow, rm *: deny } } }三档语义allow直接放行ask每次弹确认deny直接拒绝。写法上通常是「通配符兜底 具体命令覆盖」上面这段的意思是「默认所有命令都要问但只读的 git 和 ls 类命令放行删除类命令一律拒绝」。我为什么把edit也设成ask因为改文件是它最有价值的动作也是最容易出错的动作。让它每次改之前弹一下我能看到它打算改哪个文件——这个信息本身就很有价值经常我一看文件名就知道它理解偏了直接拒绝省掉一轮回滚。等用熟了、对某几类操作有把握了再逐步把常用的只读命令放宽。放宽的顺序建议是先放宽只读查询再放宽测试执行最后才考虑放宽写操作。反过来做的人早晚要在某次误删上长记性。提示不同版本里权限配置的字段结构可能有微调比如按工具名分组还是平铺。改完配置先做个无害的小实验验证生效——比如让它执行一条你明确设成deny的命令看是不是真的被拦住了。我见过配置写错导致通配符没匹配上、结果全部走默认放行的情况这种错不实验是发现不了的。6. 踩坑实录那些文档里不会写的排查思路6.1 常见问题速查现象最可能的原因排查动作启动后提示没有可用模型配置没读到或model字段的别名对不上检查配置文件路径和provider下的键名启动时报模块解析失败npm适配层包名写错或没装手动装一次该包确认能解析对话中途报错、输出截断limit里填的值超过了真实能力按服务方文档把context/output调小需要读文件时它只是「说」不「做」模型不支持稳定的工具调用换模型别改配置改完代码跑不起来但改动看着没问题上下文被压缩后记忆错乱/compact或直接新开会话重来命令全都弹出确认烦到不想用权限配置没写全走默认按上面的白名单方式放宽只读命令大仓库里响应特别慢语言服务反复重启或上下文塞太满观察进程缩小引用范围6.2 改坏了怎么救三层保险这是所有用编码代理的人都会遇到的事我把它整理成三层第一层是/undo。最快适合「刚刚那一步改歪了」。它的好处是对话和文件一起回滚代价是只能退退多了要重来。第二层是 git。这是根本保障。我的硬性习惯是让它在有未提交改动的仓库里干活之前先提交一次或者 stash 一次。这样最坏情况下git checkout就能回到干净状态损失可控。我见过有人在一个攒了三天未提交改动的仓库里让它跑重构出问题之后连「原本长什么样」都找不回来了。第三层是分步小改。让一个大任务拆成若干个可验证的小步骤每步跑一次测试、每步提交一次。这层的价值不在「出问题怎么救」而在「让问题在最小范围内暴露」。一次改十个文件再跑测试报错你都不知道是哪个文件引起的一次改一个文件跑一次问题定位几乎是即时的。6.3 几个我踩过、值得你跳过的坑坑一让它自己决定跑什么命令。早期我图省事权限全放行结果它为了「验证改动」跑了一条会改动工作区的命令把一堆无关文件搞乱了。教训是验证类的命令要你自己指定别让它自由发挥。现在我的做法是在AGENTS.md里明确写死「跑测试用这个命令不要用其他方式启动」。坑二拿它当搜索引擎用。问「这个框架怎么用」这类通用问题它给的答案质量通常不如直接查文档而且容易编。它的真正优势在你仓库的私有上下文——「我们项目里这个模块为什么这么设计」「这个函数被哪些地方调用」。把通用知识问题丢给它是浪费把仓库特有问题交给它才是正解。坑三一次性把整个仓库塞进去。有人喜欢直接说「看一下整个项目然后帮我优化」。这种指令的实际效果是上下文被无关文件塞满真正相关的部分反而被稀释而且容易触发压缩导致记忆错乱。正确姿势是用精确引用几个关键文件让它聚焦。坑四不看它读了哪些文件就开始 review 结果。它的输出质量高度依赖它读到的内容。如果它读错了文件后面的分析再漂亮也是错的。我现在养成的习惯是build 模式下每次改动前弹确认时先扫一眼文件列表判断它是不是找对了地方。坑五忽略模型差异一套配置打天下。不同模型在工具调用的稳定性、长上下文的注意力、代码风格上差别很大。换模型之后行为变了不一定是工具的问题先怀疑模型特性。7. 我现在的实际工作流长什么样说了这么多配置和坑最后把我日常的完整流程摊开讲一遍你可以直接照着改。新接手一个仓库第一步是跑一次/init生成AGENTS.md然后手动往里补三段目录用途、测试命令、绝对不能动的目录。第二步是把权限配置写进去只读命令放行写操作和删除操作拦住。第三步是配两个模型档位小的用于日常大的用于 plan 和重构。日常干活的时候遇到「这是什么」类的问题直接在 plan 模式问用把相关文件带进去问完就关不留会话。遇到「帮我改」类的任务先在 plan 模式出方案方案认可后切 build 执行执行前把仓库提交干净。改完让它在同一个会话里跑一次测试测试通过我自己再 review 一遍 diff——这一步我从来不省agent 改的东西我一个字一个字看因为它的目标函数是「让任务看起来完成」不是「让代码长期可维护」。顺便说一个后续可以扩展的方向因为 opencode 本身可以被脚本调用有非交互的运行模式和可以常驻的服务模式它其实能塞进自动化流程里。比如在提测前跑一遍「改动自查」的自定义命令把输出贴在评审说明里。这个用法我还在小范围试主要顾虑是稳定性——自动化流程里最怕的是「大部分时候对偶尔抽风」那种偶尔抽风比完全不可用还麻烦。等我把失败率的边界摸清楚了再细说。如果你只从这篇里带走一句话我希望是这句这类工具的价值不在「替你写代码」在「替你读完那些你不想读的文件」。把它当成一个读得极快、但判断力需要你校准的助手用起来会顺很多。把它当成一个可以放手不管的黑盒早晚要吃亏。