为什么要从opencode源码入手

  • OpenCode 作为开源 Coding Agent 的代表,直观展示了业界主流的 Skills 开发规范与调用逻辑。分析其源码,是理解以 ClaudeCode 为代表的新一代 Agent 核心工作流的最短路径之一,也方便我们编写高效准确的skills提高工作效率。

https://github.com/anomalyco/opencode

opencode中skills相关架构和流程

  • 三层架构
    • 客户端/界面层(Client/UI Layer):负责用户交互界面
    • 智能体内部组件(Agent Internal Components):OpenCode核心功能模块
    • 外部LLM服务(External LLM Service):对接第三方大语言模型
  • 执行流程
    • 技能发现与注册:识别并注册可用功能
    • LLM决策调用:由大语言模型选择合适技能执行
    • 内容延续生成:基于技能输出结果进行后续内容创作
External LLM Service Agent Internal Components Client/UI Layer External LLM (OpenAI/Anthropic/etc) LLM @session/llm.ts:48-264 SessionProcessor @session/processor.ts:45-401 ConfigMarkdown @config/markdown.ts:68-84 Skill @skill/skill.ts:44-135 PermissionNext @permission/next.ts:127-206 SkillTool @tool/skill.ts:8-82 ToolRegistry @tool/registry.ts:92-157 SessionPrompt @session/prompt.ts:690-726 Client/UI (TUI/Desktop/Web) External LLM (OpenAI/Anthropic/etc) LLM @session/llm.ts:48-264 SessionProcessor @session/processor.ts:45-401 ConfigMarkdown @config/markdown.ts:68-84 Skill @skill/skill.ts:44-135 PermissionNext @permission/next.ts:127-206 SkillTool @tool/skill.ts:8-82 ToolRegistry @tool/registry.ts:92-157 SessionPrompt @session/prompt.ts:690-726 Client/UI (TUI/Desktop/Web) 阶段 1: 技能发现与工具注册 扫描 .opencode/skill/ 和 .claude/skills/ loop [每个 SKILL.md 文件] 匹配到 "*": "allow" 默认规则 loop [每个技能] 每个工具的 execute 被包装 添加 plugin hooks 阶段 2: LLM 决策并调用技能工具 LLM 分析上下文 决定调用 skill 工具 接收 tool-call 事件 L:126-171 opt [仅在检测到 Doom Loop 时] 实际执行工具调用 匹配到 "*": "allow" 直接通过,无需用户交互 L:154 tool-result 事件 L:172-193 阶段 3: LLM 基于技能内容继续生成 技能内容已作为 tool-result 添加到消息历史中 基于技能指令 生成后续响应 发起会话 tools(model, agent) L:691-694 all() L:92-118 init({ agent }) L:152 Skill.all() L:9 state() L:44-126 parse(match) L:48 preprocessFrontmatter() L:17-66 { data, content } L:73-74 skills[] L:132-133 evaluate("skill", name, agent.permission) L:15 { action: "allow" } { description, parameters, execute } L:48-81 tools (包含 skill 工具) L:156 包装工具为 ai.tool() L:696-725 返回包装后的 tools 用户消息 stream(input) L:48-264 resolveTools() L:266-274 streamText({ messages, tools }) L:180-264 流式响应 包含 tool-call 事件 转发流式事件 process() L:45-401 检查 doom loop L:146-168 ask({ permission: "doom_loop" }) L:157-167 请求批准继续 批准/拒绝 结果 调用包装的 tool.execute() execute({ name }, ctx) L:713 Skill.get(params.name) L:52 skill info L:127-129 ctx.ask({ permission: "skill" }) L:59-64 evaluate("skill", name, ruleset) L:135 自动允许 parse(skill.location) L:66 preprocessFrontmatter() L:69-70 { data, content } L:73-74 { title, output, metadata } L:72-79 返回工具结果 更新 part 状态为 "completed" L:175-189 技能内容已加载 继续流式处理 继续 streamText 上下文包含技能内容 流式响应 (text/tool-call) 执行技能工作流

Skills 流程技术难点和编写建议

技术难点 难点原因 解决方案(代码位置) Skills 编写建议
1. 工具与 LLM 的双向通信协议 LLM 需要理解工具能力,Agent 需要理解 LLM 意图,需要标准化数据格式 采用 Vercel AI SDK 标准协议,使用 JSON Schema 定义参数
@session/prompt.ts:696-725
• 在 description 中明确说明输入输出格式
• 使用结构化输出(Markdown/JSON),避免自然语言式模糊描述
2. 多源技能文件的扫描与去重 支持多目录(.opencode/skill/, .claude/skills/),向上遍历目录树,同名冲突 使用 Filesystem.up() 向上查找,Record 自动覆盖,记录警告不阻止
@skill/skill.ts:78-123
• 使用唯一且描述性的 name(如 myproject-api-design
• 在 description 中说明适用范围,避免过于通用的命名
3. YAML Frontmatter 解析容错 YAML 语法严格,冒号/缩进/引号易出错,需要友好错误提示 预处理自动修复(冒号转块标量),使用 gray-matter 解析,自定义错误类型
@config/markdown.ts:17-66
• 避免在 frontmatter 值中使用冒号
• 复杂值使用引号包裹,测试 frontmatter 解析
4. 权限控制的灵活性与安全性 需要细粒度控制(allow/deny/ask),不同 agent 不同规则,初始化过滤 vs 运行时检查 双层检查(初始化过滤+执行验证),支持通配符,默认 “*”: “allow”
@tool/skill.ts:12-18, 59-64
• 在 skill 开头说明需要的权限
• 明确说明副作用,提供只读版本的 skill
5. 流式处理中的状态管理 LLM 流式输出时机不确定,需跟踪多个并发工具调用,状态转换和错误恢复 使用 Map 跟踪每个工具调用,清晰状态机(pending→running→completed/error)
@session/processor.ts:32-44, 126-221
• 将复杂任务拆分为多个步骤
• 在每个步骤后提供检查点,提供明确的完成标志
6. Doom Loop 检测与防护 LLM 可能重复调用相同工具,需要不影响正常重试的检测,何时介入询问 阈值 3 次,比较工具名和参数完全一致性,触发后请求用户确认
@session/processor.ts:146-168
• 避免模糊的成功条件,提供明确的标准
• 限制重试次数,包含明确的退出条件
7. 工具执行的插件化扩展 需要支持插件在执行前后插入逻辑,统一日志和错误处理,不影响核心功能 包装模式添加 before/after 钩子,标准化接口,支持异步插件
@session/prompt.ts:700-724
• 使用标准 Markdown 结构
• 在关键位置添加标记(如 <!-- plugin:before -->

Skills编写样例

核心设计原则

原则 说明
明确性优于灵活性 具体指令比灵活描述更有效(如 “ESLint 无错误” 而非 “代码质量好”)
结构化优于自然语言 使用标准格式(Markdown、JSON Schema)而非散文式描述
防御性设计 考虑失败场景、边界条件、退出机制
双层验证 初始化过滤 + 运行时检查,提高安全性和用户体验

编写实践示例

---
name: api-endpoint-implementation
description: Implement REST API endpoint with validation and tests. Returns file paths and test results.
permissions:
  read: allow
  write: allow
  bash: allow
max_retries: 2
---

# API Endpoint Implementation

## Permissions Required
- Read: API design docs
- Write: Create route and test files
- Bash: Run test suite

## Steps

### Step 1: Create Route Handler ✓
- [ ] Create file in `src/routes/`
- [ ] Add input validation with Zod
**Checkpoint**: File created and compiles

### Step 2: Write Tests ✓
- [ ] Create test file
- [ ] Write unit tests
**Checkpoint**: Tests written

### Step 3: Validate ✓
- [ ] Run test suite
- [ ] Check coverage > 80%
**Checkpoint**: All tests pass

## Success Criteria (MUST meet ALL)
- [ ] Route handler implemented
- [ ] Input validation added
- [ ] Tests pass with >80% coverage
- [ ] No ESLint errors

## Failure Handling
If tests fail after 2 attempts:
1. STOP retrying
2. Report error details
3. Suggest manual review

## Output Format
Returns structured Markdown with:
- File paths created
- Test results summary
- Coverage report
Logo

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

更多推荐