Solon v3.8.3

skills - Solon AI Skills(技能) 概念介绍

</> markdown
2026年1月22日 下午10:11:09

v3.8.4 后支持


Solon AI Skills(技能)。概念原型参考了 Claude Code Agent Skills 的设计思想:通过结构化的定义(元数据、指令/SOP、脚本/工具)赋予 Agent 特定领域的专家能力。

1、Solon AI Skills 概念介绍

在 Solon AI 体系中,Skill 是 ChatModel / Agent 能力的最小封装单元(就像微信平台的小程序)。它不只是工具的集合,而是一套包含 “感知、约束、执行” 的完整逻辑块。

  • 感知(Awareness):通过 isSupported 接口,Skill 能够感知用户的当前 Prompt,并决定自己是否要在本次对话中激活。
  • 约束(Instruction):通过 getInstruction 注入特定的 SOP(标准作业程序)。它告诉模型:在调用这些工具时,必须遵循哪些业务规则。
  • 执行(Execution):通过 getTools 挂载一组原子工具(FunctionTool),并自动对这些工具进行“染色”。
  • 染色(Coloring):借鉴 MCP(Model Context Protocol)思想,自动将 Skill 的元信息注入工具的元数据中。这使得大模型能够清晰地识别工具的“归属感”,从而在推理阶段(Thought)精准地建立指令与工具之间的关联。

核心特性

  1. 按需激活:支持根据用户意图、权限或上下文动态挂载,有效节省 Token 消耗并降低干扰。
  2. 指令隔离:每个 Skill 的指令在 System Message 中以独立的 Skill: Name 块呈现,结构清晰,模型依从性极高。
  3. 原子性与组合性:Skill 之间相互隔离,开发者可以像搭积木一样为 ChatModel 组合不同的技能集。

2、Skill 部署过程概述

在 Solon AI 中,一个 Skill 从定义到发挥作用经历以下生命周期:

  1. 定义阶段:开发者实现 Skill 接口,编写 getInstruction(SOP 提示词)并关联 FunctionTool(具体执行工具)。
  2. 注册阶段:通过 ChatModel.Builder 或 ChatConfig 将 Skill 实例添加到全局或特定的模型配置中。
  3. 触发阶段(Runtime):
    • 准入检查:请求发起时,框架遍历 Skill,执行 isSupported(prompt) 过滤无关技能。
    • 挂载激活:执行 onAttach 准备上下文环境。
    • 指令合并与染色:调用 injectInstruction,框架自动将所有活跃 Skill 的指令拼接到 System Message,同时将 Skill 身份注入到每个工具的 meta 数据中。
  4. 推理与执行:模型根据“染色”后的工具描述进行推理,在需要时触发工具调用。

3、具体 Skill 接口参考

package org.noear.solon.ai.chat.skill;

import org.noear.solon.Utils;
import org.noear.solon.ai.chat.prompt.ChatPrompt;
import org.noear.solon.ai.chat.tool.FunctionTool;
import org.noear.solon.ai.chat.tool.ToolProvider;
import org.noear.solon.lang.Preview;

import java.util.Collection;
import java.util.stream.Collectors;

public interface Skill extends ToolProvider {
    /**
     * 名字
     */
    default String name() {
        return this.getClass().getSimpleName();
    }
    
    /**
     * 描述
     */
    default String description(){
        return null;
    }

    /**
     * 技能元信息
     */
    default SkillMetadata metadata() {
        return new SkillMetadata(this.name(), this.description());
    }

    /**
     * 准入检查:决定该技能在当前环境下是否可用
     */
    default boolean isSupported(ChatPrompt prompt) {
        return true;
    }

    /**
     * 挂载钩子:技能被激活时触发,可用于初始化 Session
     */
    default void onAttach(ChatPrompt prompt) {
    }

    /**
     * 指令注入:转化并注入到 System Message
     */
    default String getInstruction(ChatPrompt prompt) {
        return null;
    }

    /**
     * 工具注入:转化并注入到工具列表
     */
    default Collection<FunctionTool> getTools() {
        return null;
    }


    /**
     * 注入指令
     */
    /**
     * 注入指令并对工具进行“染色”
     */
    default void injectInstruction(ChatPrompt prompt, StringBuilder combinedInstruction) {
        String ins = getInstruction(prompt);
        Collection<FunctionTool> tools = getTools();

        // 1. 如果有工具,进行元信息染色(借鉴 MCP 思想)
        if (tools != null && !tools.isEmpty()) {
            for (FunctionTool tool : tools) {
                // 将所属 Skill 的名字注入工具的 meta
                tool.metaPut("skill", name());
                // 如果需要,也可以把 Skill 的描述或其它元数据注入
                if (Utils.isNotEmpty(description())) {
                    tool.metaPut("skill_desc", description());
                }
            }
        }

        // 2. 构建 System Prompt 指令块
        if (Utils.isNotEmpty(ins) || (tools != null && !tools.isEmpty())) {
            if (combinedInstruction.length() > 0) {
                combinedInstruction.append("\n");
            }

            // 统一头部
            combinedInstruction.append("**Skill**: ").append(name());

            // 补充 Skill 描述(如果有)
            if (Utils.isNotEmpty(metadata().getDescription()) && !name().equals(metadata().getDescription())) {
                combinedInstruction.append(" (").append(metadata().getDescription()).append(")");
            }
            combinedInstruction.append("\n");

            // 注入具体指令
            if (Utils.isNotEmpty(ins)) {
                combinedInstruction.append(ins).append("\n");
            }

            // 注入工具关联说明(告知模型这些工具受此 Skill 指令约束)
            if (tools != null && !tools.isEmpty()) {
                String toolNames = tools.stream().map(t -> t.name()).collect(Collectors.joining(", "));
                combinedInstruction.append("- **Supported Tools**: ").append(toolNames).append("\n");
            }
        }
    }
}