03-Agent Skills 技能系统
OpenCode技能系统(Skills)是封装领域知识和最佳实践的可复用模块,采用标准化的目录结构和SKILL.md文件格式。主要特点包括: 目录结构规范: 支持全局和项目级配置 目录命名采用kebab-case格式 技能文件必须命名为SKILL.md SKILL.md文件格式: 包含必需元数据(name, description) 支持可选元数据(license, compatibility等)
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 是否包含
name和description -
name是否与目录名匹配 - 文件路径是否正确
- 权限是否设置为
deny
7.2 Skill 名称冲突
确保所有 Skills 的 name 唯一。如果存在同名 Skills,后加载的会覆盖先加载的。
7.3 Skill 内容过长
如果 Skill 内容过长,可以考虑:
- 拆分成多个相关 Skills
- 使用引用和链接
- 提取通用部分到单独的 Skill
8. 最佳实践
8.1 设计原则
- 单一职责:每个 Skill 专注于一个领域
- 可复用性:避免项目特定的内容
- 实用性:提供具体、可执行的指导
- 可维护性:结构清晰,易于更新
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 需要:
- 明确技能的范围和边界
- 提供详细的规范说明
- 包含实用的代码示例
- 定期更新维护
更多推荐




所有评论(0)