说实话,我一开始是把 opencode 当“带界面的命令行聊天工具”用的,直到某天它在我眼皮底下连续做了十几步操作——glob 定位文件、grep 找出全部调用点、分段读源码、用补丁改了三个模块、再跑测试收尾。整个过程不到一分钟,每一步旁边都跳出 diff 预览,我除了扫一眼确认,几乎没有插手。那一刻我才意识到,这个“外壳”真正的价值不在对话本身,而在工具、服务、模型调度这些东西怎么被编排起来。
上篇聊过安装和基础用法,这篇直接往下钻:工具链是如何与终端世界协作的,服务面有哪些入口可以接自动化,外壳机制怎么做到模型无关地换“发动机”,以及我实际把它接进工作流的三个场景。内容偏进阶,适合已经跑通 opencode 基础操作、想把它从“玩具”变成“生产力”的人。我不会把它夸成神器,但也不会避谈它在我工作流里真正发光的地方。
1. 工具调用链:opencode 在终端世界里是怎么“动手”的
1.1 内置工具清单:不只是读文件和跑命令
模型本身没有手,它只能输出文字,真正让它改变世界的是工具集。opencode 的工具数量不算多,但每一件都长在终端场景上。我按自己的使用频率排了个序:
| 工具 | 核心用途 | 我什么时候会用到它 |
|---|---|---|
| bash | 执行命令 | 装依赖、跑测试、看日志、批量操作 |
| glob | 按文件名模式搜索路径 | 快速定位 src 下的某个组件或配置文件 |
| grep | 按内容模式搜索 | 查某个函数的所有调用点、找 TODO |
| read_file | 读取文件指定区间 | 精确看某段实现、确认上下文 |
| apply_patch | 以补丁形式应用改动 | 修改或新增文件时保持最小 diff |
| 联网类工具 | 搜索文档、抓取网页 | 查 API 变更、看官方示例 |
这里最容易被新手搞混的是 glob 和 grep:一个按文件名找,一个按文件内容找。我早期经常在提示词里写反,让 agent 用 grep 找文件名,结果对方搜了个寂寞。现在我的固定习惯是“先名字后内容”——先 glob 缩小候选文件范围,再 grep 在范围内精确命中,效率高很多。
1.2 每一次工具调用都要过“确认闸门”
opencode 的每个工具调用在真正执行前都会先“报备”:参数是什么、作用于哪个路径、大概会产生什么影响,全部摊开给你看,你可以确认也可以拒绝。这种设计在改文件时尤其重要——它展示的不是“我要写文件”,而是完整的补丁预览,你能看到每一行增删再放行。
配合这层闸门的是权限模式,我记得大致有三档:read-only(只读)、write(允许读写文件)、danger-full-access(全放开)。我个人的用法是:日常探索用 read-only,让它找线索、给方案,不轻易动磁盘;准备真正改代码时切到 write;danger-full-access 很少开,只在跑在一次性容器里或者明确知道自己在干什么的时候才用。
提示:如果你发现自己总是跳过确认、直接放行所有工具调用,那就别用危险模式继续麻痹自己——权限模式的价值是“强制刹车”,不是让你一路绿灯的。
1.3 工具产出的上下文与截断策略
很多人没意识到,工具输出是 token 消耗的大头。一个cat命令把三千行日志砸进上下文,模型一会儿就开始“失忆”,后面所有推理都变形。opencode 对过长的工具输出有一套截断策略,模型通常只能看到保留的摘要或输出尾部;如果它觉得信息不够,会主动回去用更精确的参数再读一次文件。
这个机制反过来给了我们一个调教空间。我习惯在提示词里明确要求:
先 glob 定位到 docs/config 下的相关文件,再用 read_file 分段读取。 不要一次性查看整个文件,也不要无脑 cat。这么写之后,工具调用次数和 token 消耗都明显下降,模型给出的回答反而更准。原理很简单:分段读文件时,模型每次拿到的都是干净的一小段上下文,而不是被垃圾内容挤爆的残缺记忆。
1.4 我在权限模式下踩过的边界
有一次我让 agent 清理一个老项目的构建产物。它先跑了一遍构建,确认还正常,然后准备执行rm -rf清掉一个目录。确认弹出来的时候我瞄了一眼,路径写的居然不是dist,而是另一个我根本不记得的目录名。如果不是那一下强制确认,这条命令就直接跑进系统里的。后来我查了一下,是 agent 从某个历史命令里抄错了路径。
这件事之后我从不在无人值守的自动化里给 agent 全放开权限。工具链越强,越需要一个能兜住的边界。尤其是 bash 这种能执行任意命令的工具,一旦出问题不是改一行代码那么简单,而是可能顺着目录指哪打哪。
2. 服务面剖开:无头模式、本地服务与 MCP 双向通道
2.1 headless 模式:一次性任务与脚本化调用
TUI 适合人坐在终端前交互,但自动化和脚本调用需要的是另一种形态:无头模式。opencode 的 run 子命令就是干这个的,一条命令带一段自然语言描述,就能跑完一个完整任务然后退出:
opencode run "看一下 src 下有哪些未使用的导出,给出清理建议"它还能和标准输入配合,这就很 UNIX 了:
echo "帮我总结这份 log 里的异常模式" | opencode run这种模式用到爽的场景是“模板化任务”——比如生成 commit message、给某个模块的代码做初步审查、批量检查仓库里的硬编码密钥。你可以把它塞进 shell 脚本、git alias、甚至 CI 步骤里,相当于给整个系统装了一个“听得懂人话的命令行”。
2.2 内置服务与本地路由:不打开 TUI 也能用
除了命令行骨架之外,opencode 还带一个服务面:启动本地服务之后,会用 HTTP 接口暴露会话能力,同时提供一个可交互的 Web 页面,浏览器里也能直接操作。这意味着几件事:
- 不习惯终端的同事可以打开浏览器就能用
- 可以在局域网里共享一个会话入口
- 能基于它的 HTTP 接口再套一层自己的前端
我有一阵子图省事,在平板上连着局域网里的 opencode 服务,靠在沙发上处理简单的代码问答,体验还挺奇妙的。需要提醒的是,这个服务面默认监听本机,对外共享时要自己处理网络权限,毕竟暴露一个“能执行命令的代理”给局域网,安全意识还是要有的。具体端口和支持的路由以当前版本的文档为准,不同版本改动不小。
2.3 作为 MCP 客户端:把外部工具接进来
MCP 这个协议一句话解释就是:把“工具发现 + 工具调用”标准化,让 AI 应用不用为每个外部系统单独写适配。opencode 支持以 MCP 客户端的身份接入外部工具服务,这意味着它能操作文件系统、连数据库、查工单系统,只要对方实现了 MCP 协议。
配置文件里声明 MCP 服务器即可,大致格式是这样:
{ "mcp": { "my-db": { "type": "stdio", "command": ["uvx", "mcp-server-sqlite", "--db", "app.db"] } } }本地工具走 stdio 方式,远程服务走 URL 方式,两种 transport 各有用处。配置完成后重启 opencode,新的工具就出现在会话里,调用方式和内置工具一样。语言模型在这个场景里相当于一张万能遥控器,MCP 则把各种电器都变成了“同一套红外协议”。
2.4 作为 MCP 服务器:被别的 AI 工具调用
更妙的是反向路径。opencode 可以把自己的能力暴露成一个 MCP 服务端,让其他支持 MCP 的 AI 客户端把它当作“终端操作员”。我在另一个编辑器里试过这种接法:高层 AI 负责规划和理解需求,opencode 负责读代码、跑命令、改文件,两个模型各干各擅长的事。
这种“多 Agent 协作”的方向目前官方文档还比较薄,离成熟也有一段距离,但值得关注。如果你在搭自己的 AI 工作台,它会是一个不错的“手脚”节点。
3. 外壳机制:为什么换模型像换发动机一样简单
3.1 Provider 层:模型无关的归一化设计
opencode 最容易被低估的设计是:它本身不绑定任何一家模型。它不是“某模型的前端”,而是一个模型无关的调度外壳。每家模型的消息格式、工具调用格式、流式协议都不一样,opencode 在 provider 层做了一层适配和归一化,让上层会话逻辑只面对一套统一接口。
这带来一个很实际的好处:同一个会话里,你可以随时把正在用的模型换成另一个 provider,上下文不用丢,工具调度逻辑也不用改。我经常在写代码时用推理强的模型,遇到一些简单重复的改动了,临时切一个便宜快速的模型顶上,成本立刻下降。
坏处也有——新模型发布之后,适配层需要跟上才完善。遇到这种窗口期,自定义 provider 就是救命的后门。
3.2 主模型与小模型的角色分工
外壳设计里有一个容易被忽略的细节:模型分主模型(model)和小模型(small_model)。主模型负责核心推理和工具调度;小模型则处理一些“边缘翻译”活——比如给会话起标题、给工具结果做摘要、生成简短的辅助文案。
这个分工很聪明。起标题、做摘要这类任务不需要顶级智商,用便宜快速的小模型跑,能省下大量 token 费用。但我的实操建议是:小模型不能选太弱的,否则会话标题变得莫名其妙,工具摘要也丢关键信息,反而拖累主模型的判断。至少选一个质量下限有保障的轻量模型。
3.3 自定义 provider:接网关与本地模型
默认的 provider 列表覆盖了主流服务商,但真实世界的模型接入从来不会只有官方一条路。opencode 支持自定义 provider,最常见的两种诉求是:接入公司内部的统一模型网关,以及接入本地推理服务。
配置文件里声明 provider 和模型,大致长这样:
{ "provider": { "my-gateway": { "npm": "@some-scope/opencode-provider-gateway", "options": { "apiKey": "{env:GATEWAY_API_KEY}", "baseURL": "https://gateway.internal.example.com/v1" }, "model": "my-model-id" } } }apiKey 用环境变量引用的方式我很推荐——配置文件可以进仓库,密钥不进,团队成员 clone 下来补一下环境变量就能跑。
注意:具体 provider 字段在不同版本里会有差异。如果配置不生效,第一步是查当前版本里 provider 的 schema,而不是怀疑自己环境坏了。
3.4 模型元数据对工具行为的影响
同样是模型,不同型号对工具调用的支持程度差异很大。有的模型原生支持并行工具调用,有的只能串行;有的上下文窗口大,有的稍微聊深一点就烧爆。opencode 的模型配置里有对应的元数据字段(tool_call 方式、上下文限制、计费信息等),外壳会根据这些信息自动调整调度策略。
我观察到一个很有意思的现象:同一个任务换到不同模型上,工具调用的“节奏”完全不同。强模型可能一次并行调三个工具,弱模型则老老实实一个一个来,有时还要回头补查。这不是代码出 bug,而是外壳在响应模型的能力边界。配置里给模型标注准确的元数据后,这类问题会大幅减少。
4. 实战集成:我把 opencode 接进了三类工作流
4.1 场景一:终端里的 Code Review 助手
代码审查一直是 AI 在终端场景里最能立刻见效的用途。我的做法很简单:把暂存区的改动喂给 opencode,让它以 reviewer 视角输出问题清单:
git diff --cached | opencode run "请以上线前最后一道 review 的眼光看这段改动,列出: 1. 潜在 bug 2. 边界条件遗漏 3. 测试盲区 按严重程度从高到低排序,不超过 8 条。"这条命令我直接做成了 git alias,名字就叫git review。之后的流程是:先看它列出的问题,逐条在 TUI 会话里追问(比如“第三个问题具体在哪个分支路径下触发”),把 agent 当作一个看过 diff 的同事来对话。这个场景我从试用到日常只花了两天,现在它是我提交代码前的固定动作。
4.2 场景二:headless 批量处理一叠 issue
一个朋友所在的团队积压了三十多个小型 issue,人工逐个看一遍要耗掉半天。后来我把这个活拆成一个很朴素的脚本:读取 issue 列表文件,逐条喂给opencode run,把输出落盘:
while IFS= read -r issue; do opencode run "根据以下 issue 描述,定位相关代码,给出修复方案与影响面分析:$issue" | tee -a reports.log done < issues.txt注意两个细节:逐条跑而不是并行跑——并行会让不同任务的上下文混在一起,而且让 agent 同时处理多条 issue,它在代码里的改动目标容易跑偏;每条输出都落盘留档,后面人工抽检时有据可查。那批 issue 后来用了大概一个下午全部处理完,其中一半的方案直接可用,另一半给了关键线索。比预想的靠谱。
4.3 场景三:让 opencode 读取内部 API 的 MCP 服务
最重的一次集成是把 opencode 接进团队的内部工具。背景是团队有一个内部文档和质量数据平台,API 不对外开放,以前想查询要手点网页。我用 Node 写了一个非常薄的 MCP 服务,把查询接口包成工具:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const server = new McpServer({ name: "internal-tool", version: "1.0.0" }); server.registerTool( "search_docs", { keyword: z.string() }, async ({ keyword }) => { const result = await callInternalApi(`/docs?q=${encodeURIComponent(keyword)}`); return { content: [{ type: "text", text: JSON.stringify(result) }] }; } ); const transport = new StdioServerTransport(); await server.connect(transport);然后在 opencode 配置里把这个 MCP 服务挂进去,重启之后就能在会话里调用内部查询。从此 agent 可以自己查完文档再动手改代码,而不是靠我复制粘贴。这个集成让整个工作流的“自主性”上了一个台阶,因为很多问题不再需要人来中转。
4.4 接完工作流之后的体会与注意点
把这三类工作流跑顺之后,我的感受分两面。正面的是:opencode 的定位确实不是 IDE 也不是聊天框,而是一个“可编程的控制面”,把模型、工具、服务、工作流四样东西黏在一起;只要能通过 CLI 或服务接口触达的东西,理论上都能成为它的能力边界。
负面或者说需要注意的,主要是两件事。第一,headless 自动化是高风险区——没人逐步盯着确认的时候,权限边界就必须更严格。我的习惯是:能只读绝不给写,能限定路径绝不放开,危险命令一律 deny。第二,项目中要维护一份给模型的“工作约定”文件,里面写明仓库结构、常用命令、代码风格要求、prompt 规范。刚开始我不觉得这东西重要,直到发现 agent 每次都要自己摸索一遍项目布局,浪费的 token 和时间远超预期。团队一起使用时,这份约定的收益会被放大。
写到这里,回头再看开头那个“40 秒连续完成重构”的场景,我已经不觉得惊讶了——那只是工具链的正常工作方式。opencode 的外壳做到了模型无关,工具面覆盖了终端操作,服务面支持主动接入,而真正让它发挥价值的,是我们怎么把这些能力编进自己的日常流程。集成越深,越要尊重权限边界和人工 review 这条底线,毕竟它只是个聪明的执行者,兜底责任的还是人。