两种扩展机制

OpenCode 提供两种不同的扩展方式,适用于不同场景:

特性Agent SkillsCustom Tools
定义方式Markdown 文件TypeScript/JavaScript
复杂度简单中等
适用场景指令模板、工作流外部 API、复杂逻辑
参数验证无Zod Schema
可复用性项目级全局/项目级

Agent Skills

什么是 Agent Skills

Agent Skills 是通过 SKILL.md 文件定义的可复用指令模板。它们帮助 AI 理解特定的工作流程和最佳实践。

创建 Skill

在项目根目录创建 .opencode/skills/deploy/SKILL.md:

---
name: deploy
description: 部署应用到生产环境
triggers:
  - 部署
  - deploy
  - 上线
---

# 部署流程

当用户要求部署时,按以下步骤执行:

## 前置检查
1. 确认当前分支是 main
2. 运行测试:`npm test`
3. 检查构建:`npm run build`

## 部署步骤
1. 构建生产版本:`npm run build:prod`
2. 上传到服务器:`rsync -avz dist/ server:/app/`
3. 重启服务:`ssh server 'systemctl restart app'`
4. 验证部署:`curl -f https://app.example.com/health`

## 回滚
如果部署失败,执行回滚:
`ssh server 'cd /app && git checkout HEAD~1 && npm run build && systemctl restart app'`

使用 Skill

> 部署到生产环境

[AI 识别到 deploy skill]

好的,我来帮你部署。首先进行前置检查...

1. ✓ 当前分支:main
2. 运行测试...
   ✓ 15 tests passed
3. 检查构建...
   ✓ Build successful

开始部署...

Skill 目录结构

.opencode/
└── skills/
    ├── deploy/
    │   └── SKILL.md
    ├── code-review/
    │   └── SKILL.md
    └── database-migration/
        └── SKILL.md

Custom Tools

什么是 Custom Tools

Custom Tools 是用 TypeScript/JavaScript 编写的自定义工具函数,可以执行任意逻辑,包括调用外部 API、操作数据库等。

创建 Tool

创建 .opencode/tools/weather.ts:

import { z } from "zod";

export default {
  name: "get_weather",
  description: "获取指定城市的天气信息",
  parameters: z.object({
    city: z.string().describe("城市名称"),
    unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
  }),
  async execute({ city, unit }) {
    const response = await fetch(
      `https://api.weather.com/v1/${city}?unit=${unit}`
    );
    const data = await response.json();
    return {
      temperature: data.temp,
      condition: data.condition,
      humidity: data.humidity,
    };
  },
};

Tool 配置

在 config.json 中注册 Tool:

{
  "tools": {
    "custom": {
      "enabled": true,
      "paths": [".opencode/tools/*.ts"]
    }
  }
}

使用 Tool

> 北京今天天气怎么样?

[AI 调用 get_weather tool]

北京今天天气:
- 温度:22°C
- 天气:晴
- 湿度:45%

实战案例

案例一:数据库查询 Tool

import { z } from "zod";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

export default {
  name: "query_database",
  description: "执行只读 SQL 查询",
  parameters: z.object({
    sql: z.string().describe("SQL 查询语句"),
    params: z.array(z.any()).optional().describe("查询参数"),
  }),
  async execute({ sql, params }) {
    // 安全检查:只允许 SELECT
    if (!sql.trim().toUpperCase().startsWith("SELECT")) {
      throw new Error("Only SELECT queries are allowed");
    }
    const result = await pool.query(sql, params);
    return result.rows;
  },
};

案例二:Jira 集成 Skill

---
name: jira
description: 与 Jira 交互,管理任务
triggers:
  - jira
  - 任务
  - ticket
---

# Jira 操作

## 创建任务
使用 MCP 的 Jira 工具创建任务:
- 项目:PROJECT
- 类型:Task/Bug/Story
- 优先级:P0-P3

## 查询任务
查询分配给当前用户的任务,按优先级排序。

## 更新状态
更新任务状态前,确认:
1. 前置条件是否满足
2. 是否需要通知相关人员

案例三:调用 Python 脚本

import { z } from "zod";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);

export default {
  name: "run_python",
  description: "运行 Python 脚本",
  parameters: z.object({
    script: z.string().describe("Python 脚本路径"),
    args: z.array(z.string()).optional().describe("脚本参数"),
  }),
  async execute({ script, args = [] }) {
    const { stdout, stderr } = await execAsync(
      `python3 ${script} ${args.join(" ")}`
    );
    if (stderr) throw new Error(stderr);
    return stdout;
  },
};

权限配置

Tool 权限

{
  "permissions": {
    "tools": {
      "get_weather": "allow",
      "query_database": "ask",
      "run_python": "deny"
    }
  }
}

Skill 权限

Skill 中的命令也受权限控制:

{
  "permissions": {
    "commands": {
      "npm test": "allow",
      "rsync *": "ask",
      "rm *": "deny"
    }
  }
}

最佳实践

  1. 单一职责:每个 Tool/Skill 只做一件事
  2. 明确描述:description 要清晰说明用途
  3. 参数验证:使用 Zod 严格验证输入
  4. 错误处理:提供有意义的错误信息
  5. 安全意识:敏感操作需要权限确认