OpenCode 自定义工具——给AI编程助手装上专属技能

📂 技术笔记 📝 2037 字 ⏱️ 5 min

用过 Cursor、Claude Code 这些 AI 编程工具的朋友都知道,它们内置了读文件、写代码、跑命令这些基本能力。但项目一旦有定制需求——比如查数据库、调内部 API、跑特定脚本——通用工具就不够用了。

OpenCode 的自定义工具(Custom Tools)就是干这个的:你写一个简单的 TypeScript 文件,就能给 AI 装上专属技能,想让它干什么都行。

近期用 Open Code 偏多一些,用着用着就会产生各种需求,就此萌生了折腾的想法,然后就有了这篇文章以此记录下自己的学习过程。

🤔 自定义工具是什么

自定义工具就是你写的函数,LLM 在对话中可以直接调用。它们跟 OpenCode 自带的内置工具(readwritebash)平起平坐,AI 会自己判断什么时候该用。

关键是:工具定义用 TypeScript/JavaScript 写,但背后可以调用任何语言——Python、Shell、Node,随你。

📍 放哪里

工具文件两个位置二选一:

  • 项目级.opencode/tools/ 目录下,只对当前项目生效
  • 全局级~/.config/opencode/tools/,所有项目都能用

文件名就是工具名。比如 .opencode/tools/database.ts 会创建一个叫 database 的工具。

✏️ 怎么写

最简单的写法是用 tool() 辅助函数,类型安全、参数校验自动搞定:

// .opencode/tools/database.ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "查询项目数据库",
  args: {
    query: tool.schema.string().describe("要执行的 SQL 查询"),
  },
  async execute(args) {
    // 这里写你的逻辑
    return `已执行查询: ${args.query}`
  },
})

写好以后,你在对话里跟 AI 说"查一下数据库里最新的订单",AI 就会自动调用这个工具,传 SQL 进去。

📦 一个文件多个工具

一个文件可以导出多个工具,命名规则是 文件名_导出名

// .opencode/tools/math.ts
import { tool } from "@opencode-ai/plugin"

export const add = tool({
  description: "两数相加",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args) {
    return args.a + args.b
  },
})

export const multiply = tool({
  description: "两数相乘",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args) {
    return args.a * args.b
  },
})

这会生成两个工具:math_addmath_multiply

🔄 覆盖内置工具

自定义工具的优先级高于内置工具。如果你写一个叫 bash.ts 的文件,它就会替代 OpenCode 自带的 bash 工具:

// .opencode/tools/bash.ts — 替换内置 bash
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "受限的 bash 执行",
  args: {
    command: tool.schema.string(),
  },
  async execute(args) {
    return `已拦截: ${args.command}`
  },
})

如果你想禁用某个内置工具但不想覆盖,用权限配置更合适。

⚙️ 参数定义

参数类型用 tool.schema(底层是 Zod),支持各种类型校验:

args: {
  query: tool.schema.string().describe("SQL 查询"),
  limit: tool.schema.number().optional().describe("返回条数上限"),
  format: tool.schema.enum(["json", "csv"]).describe("输出格式"),
}

也可以直接导入 Zod 自己写:

import { z } from "zod"

export default {
  description: "工具描述",
  args: {
    param: z.string().describe("参数说明"),
  },
  async execute(args, context) {
    return "结果"
  },
}

🧠 获取上下文信息

工具能拿到当前会话的上下文:

// .opencode/tools/project.ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "获取项目信息",
  args: {},
  async execute(args, context) {
    const { agent, sessionID, messageID, directory, worktree } = context
    return `Agent: ${agent}, 目录: ${directory}, Git 根目录: ${worktree}`
  },
})
  • context.directory:当前工作目录
  • context.worktree:Git worktree 根目录

🐍 实战:用 Python 写工具

工具定义是 TS,但背后跑 Python 脚本完全没问题。先写 Python 逻辑:

# .opencode/tools/add.py
import sys
a = int(sys.argv[1])
b = int(sys.argv[2])
print(a + b)

再写工具定义调用它:

// .opencode/tools/python-add.ts
import { tool } from "@opencode-ai/plugin"
import path from "path"

export default tool({
  description: "用 Python 做加法",
  args: {
    a: tool.schema.number().describe("第一个数"),
    b: tool.schema.number().describe("第二个数"),
  },
  async execute(args, context) {
    const script = path.join(context.worktree, ".opencode/tools/add.py")
    const result = await Bun.$`python3 ${script} ${args.a} ${args.b}`.text()
    return result.trim()
  },
})

这里用 Bun.$ 执行 Python 脚本,你也可以用 Node 的 child_process.execSync

💡 实际场景示例

查数据库:写个 SQL 查询工具,AI 直接帮你查业务数据。

调内部 API:封装公司内部接口,AI 帮你调 API 查状态、发请求。

代码规范检查:对接 ESLint、Prettier,AI 写完代码自动跑检查。

部署触发:一键调 CI/CD 流水线,AI 帮你部署。

日志分析:写个工具 grep 日志文件,AI 直接定位 Bug。

🎯 小结

OpenCode 的自定义工具机制,把 AI 编程助手从"只能读写代码"升级到了"能对接任意系统"。关键点:

  • TypeScript 定义 + 任意语言执行
  • 文件名 = 工具名,简单直觉
  • 支持参数校验和上下文获取
  • 可覆盖内置工具
  • 一个文件导出多工具

换个说法——这不是在给 AI 写代码,这是在给 AI 装技能。 写好工具,你的 AI 助手就能做任何你想让它做的事。

🏷️ 标签

分享: