OpenCode 的 22 个内置工具:从文件操作到语义搜索
OpenCode 的 22 个内置工具:从文件操作到语义搜索
一个 AI Agent 到底能干多少事,取决于它手里有多少工具。
OpenCode 在 GitHub 上拿了 140K star,月活开发者 650 万,靠的不只是模型强——而是内置了一套完整的工具链。22 个内置工具,从读文件、写代码、搜索代码库,到跑 shell、查文档、对接 LSP 诊断,再到委派子任务、管理进度,基本覆盖了日常开发的全部动作。而且还能通过 MCP 和 Plugin 接入任意外部服务,理论上没有上限。
这篇文章带你逐个拆解这 22 个工具的设计、用法和内部机制。
本文提纲
- 工具体系总览 — 22 个工具分类速查
- 文件操作 4 件套 — read / write / edit / apply_patch
- 代码搜索三剑客 — glob / grep / codesearch
- 网络工具 — webfetch / websearch
- Bash 工具 — 执行 shell 命令
- LSP 诊断 — 30+ 语言自动接入
- Agent 管理工具 — task / plan / question / todowrite
- 权限控制系统 — Allow / Deny / Ask 三级管控
- MCP 和 Plugin 扩展 — 无限扩展能力
- 内部实现解析 — 工具接口与注册机制
工具体系总览
先上一张全景表,把 22 个工具分成 5 大类:
| 类别 | 工具 | 作用 |
|---|---|---|
| 文件操作 | read | 读取文件内容,支持行范围 |
| write | 创建新文件或覆盖已有文件 | |
| edit | 精确字符串替换修改文件 | |
| apply_patch | 应用 patch 格式的批量修改 | |
| 代码搜索 | glob | 按文件名模式匹配查找文件 |
| grep | 正则表达式搜索文件内容 | |
| codesearch | 语义化代码搜索 | |
| 网络工具 | webfetch | 抓取指定 URL 的网页内容 |
| websearch | 使用 Exa AI 搜索网页信息 | |
| Shell | bash | 执行任意 shell 命令 |
| IDE 集成 | lsp | LSP 代码智能(定义跳转、引用查找等) |
| Agent 管理 | task | 启动子 Agent 执行子任务 |
| plan | Plan 模式,只分析不修改 | |
| question | 向用户交互式提问 | |
| todowrite | 管理任务进度列表 | |
| 知识加载 | skill | 加载 Skill 文件注入上下文 |
| 安全防护 | external_directory | 外部路径访问守卫 |
| doom_loop | 重复调用检测守卫 | |
| 内置 Agent | build | 默认构建 Agent,全工具开放 |
| explore | 只读探索 Agent | |
| general | 通用子 Agent | |
| compaction | 上下文压缩 Agent |
这套工具的设计思路很清晰:先用 read / glob / grep 理解代码,用 edit / write / apply_patch 修改代码,用 bash 跑构建和测试,用 lsp 获取 IDE 级别的代码智能,用 task 拆分复杂任务,用 webfetch / websearch 查外部资料。整个开发流程中 Agent 能自主完成绝大部分操作。
文件操作 4 件套
文件操作是 Agent 最基础的能力。OpenCode 提供了 4 个工具,各有侧重:
read — 读文件
读取指定文件的内容,支持指定行范围。大文件不用一次全读,可以分段读取。底层就是 cat -n 格式,带行号输出。
read(filePath: string, offset?: number, limit?: number)一个典型的用法——先读文件头了解结构,再读关键函数:
read("src/index.ts", 0, 50) // 读前 50 行
read("src/index.ts", 120, 30) // 读 120-150 行write — 写文件
创建新文件,或者直接覆盖已有文件。适合从零创建组件、配置文件、测试文件。
write(filePath: string, content: string)注意:write 是整文件覆盖,不是增量编辑。如果你想保留原文件内容只改一部分,应该用 edit。
edit — 精确替换
edit 是 OpenCode 最常用的修改工具。它的工作方式是「找到旧字符串 → 替换成新字符串」。
edit(filePath: string, oldString: string, newString: string)比如把一个函数的返回值从 null 改成 undefined:
{
"filePath": "src/api/handler.ts",
"oldString": "return null;",
"newString": "return undefined;"
}edit 有个很实用的 replaceAll 参数,设为 true 时会替换文件中所有匹配项,适合批量重命名变量。
如果 oldString 在文件中找不到或找到多个(且没开 replaceAll),工具会报错。这个设计逼着 Agent 先 read 文件确认内容,再做精确修改,大幅降低了改错文件的概率。
apply_patch — 批量补丁
apply_patch 用于应用标准 patch 格式的修改。跟 edit 的区别在于:edit 是单点替换,apply_patch 是批量操作——可以在一次调用中同时添加、修改、删除、重命名多个文件。
apply_patch(patchText: string)patchText 里用特殊标记来区分操作类型:
*** Add File: src/new-module.ts
*** Update File: src/existing.ts
*** Move to: src/renamed.ts
*** Delete File: src/obsolete.ts适合做大规模重构时一次性提交多个文件的修改。
四个工具怎么选
| 场景 | 推荐工具 |
|---|---|
| 从零创建文件 | write |
| 修改已有文件的一处 | edit |
| 批量替换同名变量 | edit (replaceAll) |
| 多文件重构 | apply_patch |
| 只看不改 | read |
注意 write、edit、apply_patch 三个写工具共享同一个 edit 权限控制。在 opencode.json 里配置 "edit": "deny" 就会同时禁用这三个工具。
代码搜索三剑客
在陌生代码库里找东西,是 Agent 最高频的操作之一。OpenCode 提供了 3 个搜索工具,从文件名到内容到语义,层层递进。
glob — 文件名匹配
用 glob 模式查找文件路径。底层用的 ripgrep,默认遵守 .gitignore 规则。
glob(pattern: string, path?: string)常用模式:
**/*.ts // 所有 TypeScript 文件
src/**/*.test.ts // src 下所有测试文件
*.config.js // 根目录的配置文件返回结果按修改时间排序,最近改过的文件排前面。这个排序很聪明——Agent 处理问题时大概率需要看最近改过的文件。
grep — 内容搜索
用正则表达式搜索文件内容。同样基于 ripgrep,支持完整的 regex 语法,还能按文件类型过滤。
grep(pattern: string, path?: string, include?: string)比如在 TypeScript 文件里找所有 TODO 注释:
{
"pattern": "TODO|FIXME|HACK",
"include": "*.ts",
"path": "src"
}返回文件路径和行号,按修改时间排序。
codesearch — 语义搜索
codesearch 是更高级的搜索方式。不同于 glob 找文件名、grep 找精确文本,codesearch 能理解自然语言描述的意图去搜索代码。
比如你问 "Where is user authentication handled?",codesearch 能找到相关的认证函数和中间件,即使代码里没有 "authentication" 这个词。
三者对比
| 工具 | 搜索对象 | 查询方式 | 适用场景 |
|---|---|---|---|
| glob | 文件路径 | 通配符模式 | 找某类文件在哪 |
| grep | 文件内容 | 正则表达式 | 找某个函数/变量/字符串 |
| codesearch | 文件内容 | 自然语言 | 按意图找相关代码 |
三者底层都用了 ripgrep。如果需要搜索 .gitignore 里的文件,在项目根目录创建 .ignore 文件来覆盖:
!node_modules/
!dist/网络工具
Agent 不应该只待在本地代码库里,有时候需要查文档、搜 API、看最新的 issue。OpenCode 提供了 2 个网络工具。
webfetch — 抓取网页内容
给一个 URL,返回网页内容。支持 markdown、text、html 三种格式输出。
webfetch(url: string, format?: "markdown" | "text" | "html")典型用法:
webfetch("https://docs.example.com/api-reference", "markdown")Agent 用它来查阅最新文档、读取 GitHub README、获取 API 响应。因为 LLM 的训练数据有截止日期,webfetch 是 Agent 获取"当下"信息的关键通道。
websearch — 网页搜索
websearch 用 Exa AI 做网页搜索,输入查询语句,返回相关结果。不需要 API key——OpenCode 直接连接 Exa AI 的托管服务。
websearch(query: string)这个工具需要 OPENCODE_ENABLE_EXA=true 环境变量才能启用:
OPENCODE_ENABLE_EXA=1 opencode一个简单的区分原则:websearch 用来发现信息(搜),webfetch 用来获取内容(取)。先用 websearch 搜 "How to configure OAuth2 in Express",再用 webfetch 去读搜索结果里的具体文章。
Bash 工具
bash 是 OpenCode 里最强大的工具,也是最需要权限管控的。它能执行任何 shell 命令:
bash(command: string, timeout?: number, workdir?: string, description?: string)Agent 用它跑 npm install、git status、pytest、docker build 等命令。timeout 参数控制超时时间(毫秒),默认 120 秒。
安全限制
OpenCode 对 bash 工具有几个内置的安全措施:
- timeout 控制:命令超时自动终止,防止 Agent 卡死
- workdir 隔离:默认在项目根目录执行,可以用 workdir 切换
- 输出截断:超过 2000 行或 51200 字节的输出会被截断,防止上下文爆炸
- 权限管控:可以按命令模式精细控制,比如允许
git status但拒绝rm -rf
截断这点很关键。一个 find / 的输出可能有几十万行,全部塞进上下文会直接爆掉。OpenCode 在工具结果层面做了截断,超长输出会写入临时文件,Agent 可以用 read 工具分段读取。
Bash 的替代方案
有时候你不需要 bash:
- 读文件内容 → 用 read,不要
cat file - 搜文件名 → 用 glob,不要
find . -name "*.ts" - 搜文件内容 → 用 grep,不要
grep -r "pattern" .
专用工具比 bash 更高效,因为输出格式是结构化的,Agent 解析更准确。bash 是万能的兜底方案,但不是第一选择。
LSP 诊断 — 30+ 语言自动接入
这是我用 OpenCode 最惊喜的功能之一。
OpenCode 内置了 30+ 语言的 LSP server 支持,检测到对应扩展名的文件就自动启动 LSP,然后通过 lsp 工具提供 IDE 级别的代码智能。
支持的语言
包括但不限于:TypeScript、Go、Rust、Python(pyright)、Java(jdtls)、C/C++(clangd)、C#、Kotlin、Swift、Ruby、PHP、Dart、Elixir、Haskell、Lua、Svelte、Vue、Astro、Terraform、Nix 等。
大部分 LSP server 会自动安装——你不需要手动配置任何东西。只要项目里有对应语言的文件,OpenCode 就能提供代码智能。
lsp 工具的 9 个操作
lsp 工具支持以下操作:
| 操作 | 说明 |
|---|---|
| goToDefinition | 跳转到定义 |
| findReferences | 查找所有引用 |
| hover | 悬停查看类型信息 |
| documentSymbol | 当前文件的符号列表 |
| workspaceSymbol | 工作区符号搜索 |
| goToImplementation | 跳转到实现 |
| prepareCallHierarchy | 查看调用层级 |
| incomingCalls | 谁调用了这个函数 |
| outgoingCalls | 这个函数调用了谁 |
需要设置 OPENCODE_EXPERIMENTAL_LSP_TOOL=true 来启用:
OPENCODE_EXPERIMENTAL=true opencode诊断结果如何影响 Agent 决策
LSP 诊断会在文件编辑后自动触发。如果 Agent 修改了 TypeScript 代码,LSP 会立刻检查类型错误。诊断结果(错误、警告)会反馈给 Agent,Agent 看到报错就知道要修复。
这个实时反馈循环让 Agent 的代码质量大幅提升。没有 LSP 的时候,Agent 改完代码只能靠跑测试来发现问题;有了 LSP,类型错误在编辑阶段就能被发现和修复。
自定义 LSP
如果 OpenCode 内置的 LSP 配置不满足需求,可以在 opencode.json 里覆盖:
{
"lsp": {
"typescript": {
"disabled": true
},
"custom-lsp": {
"command": ["custom-lsp-server", "--stdio"],
"extensions": [".custom"]
}
}
}Agent 管理工具
复杂任务需要拆分、需要交互、需要记录进度。OpenCode 提供了 4 个 Agent 管理工具。
task — 子任务委派
task 工具让 Agent 可以启动子 Agent 来并行执行子任务。主 Agent 像项目经理一样分配工作,子 Agent 各自独立完成。
OpenCode 内置了几个专用子 Agent:
- explore:只读 Agent,只能读取和搜索代码,不能修改。适合快速浏览代码库
- general:通用 Agent,除了 todo 之外有完整工具权限。可以并行处理多个独立任务
在对话中用 @ 提及子 Agent 来手动调用:
@explore 帮我找出 src/api 目录下所有的路由处理函数或者让主 Agent 自动决定什么时候委派。task 工具的权限可以精细控制——你可以指定某个 Agent 只能调用特定的子 Agent。
plan — Plan 模式
Plan 模式是 OpenCode 的安全阀。在 Plan 模式下,Agent 可以分析和规划,但不能修改任何文件或执行命令。所有写操作和 bash 命令都需要用户确认。
按 Tab 键在 Build 模式和 Plan 模式之间切换。右下角会显示当前模式。
实际使用中,我习惯先切到 Plan 模式让 Agent 分析一下方案,确认没问题再切回 Build 模式执行。这样既能利用 Agent 的分析能力,又不会担心它乱改代码。
question — 交互式提问
Agent 在执行任务过程中可能会遇到模糊的指令或需要做选择的场景。question 工具让 Agent 能向你提问。
question(header: string, questions: Question[])每个问题包含标题、问题文本和选项列表。你可以从选项中选择,也可以自由输入答案。如果有多个问题,可以逐个回答后一起提交。
这个设计解决了 Agent 「蒙着眼干活」的问题——遇到拿不准的地方,它会问你,而不是自己猜。
todowrite — 任务进度管理
todowrite 让 Agent 可以创建和更新任务列表,跟踪多步骤操作的进度。
todowrite(todos: TodoItem[])Agent 用它来组织复杂任务。比如重构一个模块时,Agent 会创建 todo 列表:1) 读现有代码 → 2) 写新实现 → 3) 更新测试 → 4) 跑测试验证。每完成一步就标记一个。
注意 todowrite 默认对子 Agent 禁用。你可以手动在 Agent 配置里开启它。
权限控制系统
22 个工具全开固然强大,但安全怎么办?OpenCode 的权限系统给了你精细的控制粒度。
Allow / Deny / Ask 三级权限
每个工具都可以设置为三种状态:
- allow:自动执行,不需要确认
- ask:每次执行前询问用户
- deny:直接禁止
在 opencode.json 里配置:
{
"permission": {
"bash": "ask",
"edit": "allow",
"webfetch": "deny"
}
}Per-tool 精细规则
权限不只是全局开关,还支持基于输入的模式匹配:
{
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"npm *": "allow",
"rm *": "deny"
},
"edit": {
"*": "deny",
"packages/web/src/content/docs/*.mdx": "allow"
}
}
}规则按顺序匹配,最后匹配的规则生效。常见模式是把通配符 "*" 放前面,具体规则放后面。
安全防护机制
除了工具权限,OpenCode 还有两个安全守卫:
external_directory:当 Agent 尝试访问项目目录以外的路径时触发。默认是 ask,防止 Agent 读取或修改项目外的文件。
doom_loop:当同一个工具以相同参数重复调用 3 次时触发。这是防止 Agent 陷入死循环的保护机制。默认是 ask,Agent 卡住时会弹出来让你介入。
Per-agent 覆盖
权限可以按 Agent 覆盖。比如全局允许 bash,但只给 plan Agent 禁用:
{
"permission": {
"bash": "allow"
},
"agent": {
"plan": {
"permission": {
"bash": "deny"
}
}
}
}Agent 的权限规则跟全局配置合并,Agent 的规则优先。
.env 文件保护
OpenCode 默认保护 .env 文件不被读取:
{
"permission": {
"read": {
"*": "allow",
"*.env": "deny",
"*.env.*": "deny",
"*.env.example": "allow"
}
}
}敏感信息不会意外泄露到 Agent 的上下文里。
MCP 和 Plugin 扩展
22 个内置工具只是起点。OpenCode 支持两种扩展方式:MCP 和 Plugin。
MCP 工具动态加载
MCP(Model Context Protocol)是 OpenCode 接入外部服务的主要方式。添加一个 MCP server,它的所有工具就自动对 Agent 可用。
配置方式很简单,在 opencode.json 里加一个 mcp 块:
{
"mcp": {
"sentry": {
"type": "remote",
"url": "https://mcp.sentry.dev/mcp",
"oauth": {}
}
}
}MCP 支持两种类型:
- local:本地进程,通过 command 启动。比如
["npx", "-y", "my-mcp-server"] - remote:远程 HTTP 服务,通过 URL 连接。支持 OAuth 自动认证
添加后,MCP server 的工具会以 serverName_toolName 的格式注册。比如 Sentry MCP 的工具会变成 sentry_listIssues、sentry_getIssueDetails 等。
几个常用的 MCP server:
| MCP Server | 用途 |
|---|---|
| Sentry | 查询错误监控数据 |
| Context7 | 搜索技术文档 |
| Grep by Vercel | 搜索 GitHub 上的代码示例 |
MCP 工具同样受权限系统控制。可以用通配符管理:"sentry_*": "ask" 会把 Sentry 的所有工具设为需要确认。
Plugin 自定义工具
Plugin 是更深层的扩展机制。它不是一个工具,而是一个钩子系统,可以拦截和修改 OpenCode 的各种行为。
Plugin 文件放在 .opencode/plugins/ 目录(项目级)或 ~/.config/opencode/plugins/(全局级),也可以从 npm 安装:
{
"plugin": ["opencode-helicone-session", "opencode-wakatime"]
}Plugin 能做的事情包括:
- 添加自定义工具:用
tool()helper 创建新工具 - 拦截工具执行:
tool.execute.before和tool.execute.after钩子 - 监听事件:文件编辑、LSP 诊断、session 状态等 30+ 种事件
- 注入环境变量:
shell.env钩子
一个实际的例子——自动转义 bash 命令中的特殊字符:
export const SafeBash = async (ctx) => {
return {
"tool.execute.before": async (input, output) => {
if (input.tool === "bash") {
output.args.command = escapeShellChars(output.args.command)
}
},
}
}无限扩展能力
MCP + Plugin 的组合意味着 OpenCode 的工具集理论上没有上限。你需要数据库查询?加个 MCP。需要对接内部 API?写个 Plugin。需要自定义代码审查规则?创建一个专用 Agent 配合权限控制。
工具注册在运行时动态完成。MCP server 启动时暴露工具列表,OpenCode 自动注册并纳入 Agent 的可调用范围。整个过程不需要重启。
内部实现解析
了解工具怎么工作的,能帮你更好地使用和调试。
工具接口设计
每个工具都遵循统一的接口:
interface Tool {
name: string
description: string
args: Record
execute(args: any, context: ToolContext): Promise
} name 是工具标识,description 告诉 LLM 这个工具干什么,args 用 Zod schema 做参数校验,execute 是实际执行逻辑。
ToolContext 提供了当前会话信息:Agent 名称、session ID、消息 ID、工作目录、git worktree 路径等。工具可以基于这些上下文做出智能决策。
工具注册机制
OpenCode 的工具注册分三层:
- 内置工具:编译时注册,bash、read、edit 等 13 个核心工具
- MCP 工具:运行时动态注册。MCP server 启动后暴露工具列表,OpenCode 自动注册
- Plugin/自定义工具:从
.opencode/tools/或 Plugin 的tool()helper 注册
注册时工具名不能重复。如果自定义工具和内置工具同名,自定义工具优先。这意味着你可以完全替换内置的 bash 工具。
工具结果处理
工具执行结果会做两个处理:
截断(truncate):超过 2000 行或 51200 字节的输出会被截断。全文写入临时文件,Agent 可以用 read 分段读取。这个机制防止了上下文爆炸。
权限检查:每次工具调用前都会检查权限。检查流程是:先查 Agent 级别的权限配置,再查全局配置,最后用默认值。匹配规则按声明顺序,最后匹配的生效。
ripgrep 底层
glob 和 grep 底层用的是 ripgrep(rg)。ripgrep 默认遵守 .gitignore 规则,这意味着 node_modules/、dist/、.git/ 等目录会被自动排除。
如果需要搜索这些目录,在项目根目录创建 .ignore 文件来覆盖:
!node_modules/
!dist/
!build/这个小文件能让搜索覆盖更广的范围,但要小心——搜索 node_modules 可能返回大量结果,触发截断机制。
作者: itech001 来源: 公众号:AI人工智能时代 主页: https://www.theaiera.cn(每日分享最前沿的AI新闻和技术)
关注公众号,获取更多 AI 技术干货!