Agent Skills 技能系统

opencode专栏其他内容:
OpenCode命令使用指南
OpenCode接入千问模型指南
01-OpenCode 智能体系统概述(精炼版)
02- 创建自定义智能体完全指南(精炼版)

1. 什么是 Skills

Skills(技能)是 OpenCode 中用于封装特定领域知识和最佳实践的可复用模块。它们可以被智能体动态加载和使用,从而:

  • 标准化开发流程:提供统一的技术规范和代码模板
  • 减少重复提示词:将通用知识提取到 Skills 中
  • 提高输出质量:确保智能体遵循最佳实践
  • 易于维护更新:一处修改,全局生效

1.1 Skills 与智能体的关系

用户任务
    ↓
智能体(Agent)
    ↓
加载相关 Skills
    ↓
结合 Skills 知识 + 智能体能力 → 执行任务

智能体负责决策和执行,Skills 提供领域知识和规范。

2. Skills 目录结构

2.1 文件位置

Skills 可以放在以下位置:

全局配置(推荐)
  • 路径~/.config/opencode/skills/<skill-name>/SKILL.md
  • 作用:所有项目都可以使用
  • 适用场景:团队共享的技术规范
项目配置
  • 路径项目根目录/.opencode/skills/<skill-name>/SKILL.md
  • 作用:仅当前项目可用
  • 适用场景:项目特定的技术规范
Claude 兼容路径
  • 全局~/.claude/skills/<skill-name>/SKILL.md
  • 项目.claude/skills/<skill-name>/SKILL.md
Agents 兼容路径
  • 全局~/.agents/skills/<skill-name>/SKILL.md
  • 项目.agents/skills/<skill-name>/SKILL.md

2.2 目录命名规范

skills/
├── java-api/              # 技能目录名(kebab-case)
│   └── SKILL.md          # 技能文件(必须全大写)
├── python-api/
│   └── SKILL.md
├── vue-components/
│   └── SKILL.md
└── api-integration/
    └── SKILL.md

命名规则

  • 技能目录名:小写字母、数字、连字符(java-api, react-hooks
  • 文件名:必须命名为 SKILL.md(全大写)
  • 名称长度:1-64 个字符
  • 格式:^[a-z0-9]+(-[a-z0-9]+)*$

3. SKILL.md 文件格式

3.1 基本结构

---
name: skill-name               # 技能名称(必需)
description: 技能描述          # 技能描述(必需)
license: MIT                   # 许可证(可选)
compatibility: opencode        # 兼容性(可选)
metadata:                      # 元数据(可选)
  author: your-name
  version: "1.0.0"
---

# 技能标题

技能内容的详细说明...

## 章节1

具体内容...

## 章节2

具体内容...

3.2 Frontmatter 详解

name(必需)
  • 作用:技能的唯一标识符
  • 规则
    • 1-64 个字符
    • 小写字母、数字、连字符
    • 必须匹配目录名
  • 示例name: java-api
description(必需)
  • 作用:技能的简要描述
  • 长度:1-1024 个字符
  • 用途:帮助智能体选择合适的技能
  • 示例description: Java Spring Boot RESTful API开发最佳实践
license(可选)
  • 作用:指定技能的许可证
  • 示例license: MIT
compatibility(可选)
  • 作用:指定兼容性
  • 示例compatibility: opencode
metadata(可选)
  • 作用:自定义元数据
  • 类型:字符串键值对
  • 示例
    metadata:
      author: "Your Name"
      version: "1.0.0"
      tags: "java,spring,api"
    

3.3 内容编写规范

标题层级
  • 使用 # 作为主标题
  • 使用 ## 作为主要章节
  • 使用 ### 作为子章节
  • 保持层级清晰,不要跳级
代码示例
  • 使用代码块展示示例代码
  • 指定语言以获得语法高亮
  • 示例要完整且可运行

RESTful API 设计

URL 设计规范

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping
    public ApiResponse<List<UserDTO>> list() {
        // 实现...
    }
}
列表和表格
  • 使用有序列表(1. 2. 3.)表示步骤
  • 使用无序列表(- 或 *)表示要点
  • 使用表格展示对比信息

4. 创建 Skills 实战

4.1 创建 Java RESTful API Skill

步骤 1:创建目录和文件

mkdir -p ~/.config/opencode/skills/java-api
touch ~/.config/opencode/skills/java-api/SKILL.md

步骤 2:编写 SKILL.md

---
name: java-api
description: Java Spring Boot RESTful API开发最佳实践,包括JWT认证、统一响应格式和分层架构
license: MIT
compatibility: opencode
metadata:
  author: AI Assistant
  version: "1.0.0"
---

# Java RESTful API 开发规范

## 项目结构

src/main/java/com/example/project/
├── controller/          # 控制器层:处理HTTP请求
├── service/            # 业务层:实现业务逻辑
│   └── impl/          # 实现类
├── repository/ 或 mapper/  # 数据访问层
├── entity/ 或 model/   # 实体类
├── dto/ 或 vo/        # 数据传输对象
├── config/            # 配置类
└── util/              # 工具类

RESTful API 设计规范

URL 设计

  • 使用名词复数:/api/users 而非 /api/getUsers
  • 使用 HTTP 方法表示操作:
    • GET /api/users - 获取列表
    • GET /api/users/{id} - 获取详情
    • POST /api/users - 创建
    • PUT /api/users/{id} - 更新
    • DELETE /api/users/{id} - 删除
  • 使用连字符:/api/user-profiles
  • 分页参数:?page=0&size=10&sort=name,asc

HTTP 状态码

  • 200 OK - 成功
  • 201 Created - 创建成功
  • 204 No Content - 删除成功,无返回体
  • 400 Bad Request - 请求参数错误
  • 401 Unauthorized - 未认证
  • 403 Forbidden - 无权限
  • 404 Not Found - 资源不存在
  • 409 Conflict - 资源冲突
  • 422 Unprocessable Entity - 验证错误
  • 500 Internal Server Error - 服务器错误

统一响应格式

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResponse<T> {
    private Integer code;
    private T data;
    private String message;
    private Long timestamp;
    
    public static <T> ApiResponse<T> success(T data) {
        return ApiResponse.<T>builder()
            .code(200)
            .data(data)
            .message("success")
            .timestamp(System.currentTimeMillis())
            .build();
    }
    
    public static <T> ApiResponse<T> error(Integer code, String message) {
        return ApiResponse.<T>builder()
            .code(code)
            .message(message)
            .timestamp(System.currentTimeMillis())
            .build();
    }
}

JWT 认证集成

依赖 (pom.xml)

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.3</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.3</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.3</version>
    <scope>runtime</scope>
</dependency>

JWT 工具类

@Component
public class JwtUtil {
    @Value("${jwt.secret}")
    private String secret;
    
    @Value("${jwt.expiration:86400000}")
    private Long expiration;
    
    private SecretKey getSigningKey() {
        return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
    }
    
    public String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);
        
        return Jwts.builder()
            .subject(username)
            .issuedAt(now)
            .expiration(expiryDate)
            .signWith(getSigningKey())
            .compact();
    }
    
    public String extractUsername(String token) {
        Claims claims = Jwts.parser()
            .verifyWith(getSigningKey())
            .build()
            .parseSignedClaims(token)
            .getPayload();
        return claims.getSubject();
    }
    
    public boolean validateToken(String token) {
        try {
            Jwts.parser()
                .verifyWith(getSigningKey())
                .build()
                .parseSignedClaims(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

Controller 层规范

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    
    @GetMapping
    public ApiResponse<Page<UserDTO>> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size) {
        return ApiResponse.success(userService.findAll(page, size));
    }
    
    @GetMapping("/{id}")
    public ApiResponse<UserDTO> getById(@PathVariable Long id) {
        return ApiResponse.success(userService.findById(id));
    }
    
    @PostMapping
    public ApiResponse<UserDTO> create(@Valid @RequestBody UserCreateRequest request) {
        return ApiResponse.success(userService.create(request));
    }
    
    @PutMapping("/{id}")
    public ApiResponse<UserDTO> update(@PathVariable Long id, 
                                       @Valid @RequestBody UserUpdateRequest request) {
        return ApiResponse.success(userService.update(id, request));
    }
    
    @DeleteMapping("/{id}")
    public ApiResponse<Void> delete(@PathVariable Long id) {
        userService.delete(id);
        return ApiResponse.success(null);
    }
}


## 命名规范

- **Controller**: `XxxController`
- **Service 接口**: `XxxService`
- **Service 实现**: `XxxServiceImpl`
- **Repository**: `XxxRepository` 或 `XxxMapper`
- **DTO**: `XxxRequest`, `XxxResponse`, `XxxDTO`
- **Entity**: `Xxx` 或 `XxxEntity`

4.2 创建 Vue Components Skill

---
name: vue-components
description: Vue3 TypeScript组件开发最佳实践,包括Composition API、Pinia状态管理和组件设计模式
---

# Vue3 TypeScript 组件开发规范

## 项目结构


src/
├── components/              # 可复用组件
│   ├── common/             # 通用基础组件
│   ├── layout/             # 布局组件
│   └── business/           # 业务组件
├── views/ 或 pages/        # 页面级组件
├── stores/                 # Pinia状态管理
│   ├── auth.ts
│   ├── user.ts
│   └── index.ts
├── api/ 或 services/       # API服务
│   ├── axios.ts            # axios配置
│   ├── auth.ts
│   └── user.ts
├── types/                  # TypeScript类型定义
│   ├── api.ts
│   ├── user.ts
│   └── auth.ts
├── composables/            # 可组合函数
│   └── useAuth.ts
├── router/                 # 路由配置
└── utils/                  # 工具函数

组件开发规范

单文件组件结构

<template>
  <!-- 模板 -->
</template>

<script setup lang="ts">
// 1. imports
import { ref, computed, onMounted } from 'vue'
import type { PropType } from 'vue'

// 2. types/interfaces
interface User {
  id: number
  name: string
  email: string
}

// 3. props & emits
const props = defineProps<{
  title: string
  user?: User
}>()

const emit = defineEmits<{
  submit: [data: User]
  cancel: []
}>()

// 4. state
const loading = ref(false)
const error = ref<string | null>(null)
const formData = reactive<User>({
  id: 0,
  name: '',
  email: ''
})

// 5. computed
const isValid = computed(() => {
  return formData.name.length > 0 && formData.email.includes('@')
})

// 6. methods
const handleSubmit = async () => {
  if (!isValid.value) return
  
  loading.value = true
  try {
    emit('submit', formData)
  } finally {
    loading.value = false
  }
}

// 7. lifecycle
onMounted(() => {
  if (props.user) {
    Object.assign(formData, props.user)
  }
})
</script>

<style scoped>
/* 样式 */
</style>

Pinia Store 规范

// stores/auth.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { authApi } from '@/api/auth'
import type { User, LoginRequest } from '@/types/auth'

export const useAuthStore = defineStore('auth', () => {
  // State
  const user = ref<User | null>(null)
  const token = ref<string | null>(localStorage.getItem('token'))
  const loading = ref(false)
  const error = ref<string | null>(null)

  // Getters
  const isAuthenticated = computed(() => !!token.value)
  const isAdmin = computed(() => user.value?.role === 'admin')

  // Actions
  const login = async (credentials: LoginRequest) => {
    loading.value = true
    error.value = null
    
    try {
      const res = await authApi.login(credentials)
      if (res.code === 200) {
        token.value = res.data.access_token
        localStorage.setItem('token', res.data.access_token)
        return true
      } else {
        error.value = res.message
        return false
      }
    } catch (e) {
      error.value = '登录失败,请重试'
      return false
    } finally {
      loading.value = false
    }
  }

  const logout = () => {
    user.value = null
    token.value = null
    localStorage.removeItem('token')
  }

  return { user, token, loading, error, isAuthenticated, isAdmin, login, logout }
})

API 封装规范

Axios 配置

// api/axios.ts
import axios from 'axios'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
  timeout: 10000,
})

// 请求拦截器:添加 JWT token
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截器:统一处理错误
api.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response?.status === 401) {
      // token 过期,清除登录状态
      const authStore = useAuthStore()
      authStore.logout()
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

export default api

5. Skills 的使用方式

5.1 自动发现

OpenCode 会自动发现并加载所有可用的 Skills。智能体在执行任务时,会根据自己的职责选择合适的 Skills。

5.2 手动加载

智能体可以通过 skill 工具手动加载 Skills:

skill({ name: "java-api" })

加载后,智能体将获得该 Skills 中的所有知识和规范。

5.3 智能体配置中使用

在智能体配置中指定可用的 Skills:

permission:
  skill:
    "java-api": allow
    "vue-components": allow

6. Skills 权限控制

6.1 全局权限配置

opencode.json 中配置:

{
  "permission": {
    "skill": {
      "*": "allow",
      "internal-*": "deny",
      "experimental-*": "ask"
    }
  }
}

6.2 按智能体配置

在智能体 frontmatter 中配置:

permission:
  skill:
    "java-api": allow
    "python-api": deny

6.3 权限值说明

权限值 行为
allow 立即加载
deny 对智能体隐藏,拒绝访问
ask 加载前询问用户

7. 故障排查

7.1 Skill 没有显示

检查清单

  • 文件名是否为 SKILL.md(全大写)
  • frontmatter 是否包含 namedescription
  • name 是否与目录名匹配
  • 文件路径是否正确
  • 权限是否设置为 deny

7.2 Skill 名称冲突

确保所有 Skills 的 name 唯一。如果存在同名 Skills,后加载的会覆盖先加载的。

7.3 Skill 内容过长

如果 Skill 内容过长,可以考虑:

  • 拆分成多个相关 Skills
  • 使用引用和链接
  • 提取通用部分到单独的 Skill

8. 最佳实践

8.1 设计原则

  1. 单一职责:每个 Skill 专注于一个领域
  2. 可复用性:避免项目特定的内容
  3. 实用性:提供具体、可执行的指导
  4. 可维护性:结构清晰,易于更新

8.2 内容建议

  • 提供完整的代码示例
  • 包含错误处理方案
  • 说明最佳实践和常见陷阱
  • 保持与实际技术栈同步更新

8.3 版本管理

建议在 metadata 中记录版本信息:

metadata:
  version: "1.0.0"
  lastUpdated: "2024-01-15"
  compatibleWith:
    - "spring-boot: 3.x"
    - "java: 17+"

9. 总结

Skills 是 OpenCode 智能体系统的核心组件,通过合理设计和使用 Skills,可以:

  • 标准化团队开发流程
  • 提高代码质量和一致性
  • 减少重复的配置和提示词
  • 实现知识的积累和复用

创建好的 Skills 需要:

  1. 明确技能的范围和边界
  2. 提供详细的规范说明
  3. 包含实用的代码示例
  4. 定期更新维护
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐