Solon v3.8.3

skills - 技能的两种构建方式

</> markdown
2026年1月22日 下午11:12:40

在 Solon AI 中,技能的构建遵循“由简入繁”的原则。提供了两种构建方式:

  • 通过 SkillDesc 描述式构建
  • 通过实现 Skill 接口

无论是申明式还是接口式,开发者都能获得对技能生命周期的完整控制权。

1、通过 SkillDesc(技能描述) 描述式构建

SkillDesc 提供了一种轻量级的构建方式。它不仅支持静态属性设置,还允许通过 Lambda 注入动态谓词和逻辑。这使得开发者无需创建新的类文件,就能实现复杂的准入检查和指令生成。

适用场景:快速组合工具、逻辑相对集中的技能定义、偏好函数式风格的开发。

示例代码:

Skill skill = new SkillDesc("order_expert")
        .description("订单助手")
        // 动态准入:只有提到“订单”时才激活
        .isSupported(prompt -> prompt.getUserContent().contains("订单"))
        // 动态指令:根据用户是否是 VIP 注入不同 SOP
        .instruction(prompt -> {
            if ("VIP".equals(prompt.attr("user_level"))) {
                return "这是尊贵的 VIP 客户,请优先调用 fast_track_tool。";
            }
            return "按常规流程处理订单查询。";
        })
        .toolAdd(new OrderTools());

提示:此方式适合动态构建。

2、通过实现 Skill 接口(一般使用 AbsSkill)

通过实现 Skill 接口,开发者可以利用面向对象的特性(如继承、成员变量状态、复杂的依赖注入)来组织代码。这种方式在逻辑极其复杂、或者需要复用基础技能逻辑时最为合适。

适用场景:

  • 高度工程化:需要利用容器组件的注入能力。
  • 状态维护:需要在 onAttach 中计算并存储中间状态供后续接口使用。
  • 逻辑复用:通过继承 BaseSkill 实现一套通用的安全审计或日志逻辑。

示例代码(使用 AbsSkill 作为基类,可以简化开发):

@Component
public class TechSupportSkill extends AbsSkill {
    @Inject
    private KbService kbService; 

    @Override
    public String name() {
        return "tech_support";
    }

    @Override
    public String description() {
        return "技术支持专家技能,支持故障排查与配置重置";
    }

    @Override
    public boolean isSupported(ChatPrompt prompt) {
        String content = prompt.getUserContent();
        // 扩展意图识别:涵盖关键词、报错信息等特征
        return content != null && (content.contains("故障") || content.contains("报错") || content.contains("error"));
    }

    @Override
    public String getInstruction(ChatPrompt prompt) {
        return "你现在是技术支持专家,请遵循以下 SOP:\n" +
                "1. 首先根据报错信息检索知识库(search_kb)。\n" +
                "2. 给出方案前,必须核实用户的环境版本(Solon 版本)。\n" +
                "3. 若需修改生产配置,必须明确告知风险。";
    }

    @ToolMapping(name = "search_kb", description = "搜索技术知识库,获取故障解决方案")
    public String searchKb(@Param("query") String query) {
        if (Utils.isEmpty(query)) {
            return "请输入搜索关键词";
        }
        return kbService.search(query);
    }

    @ToolMapping(
            name = "reset_config",
            description = "重置系统配置(高危操作)",
            meta = "{'danger': true, 'confirm_msg': '操作将导致服务重启,确定吗?'}"
    )
    public String resetConfig(@Param("serviceName") String serviceName) {
        // 执行具体的重置逻辑
        return "服务 [" + serviceName + "] 配置已重置,正在重启...";
    }
}

AbsSkill 基类(也可定义自己的快捷基类):

public class AbsSkill implements Skill {
    protected final SkillMetadata metadata;
    protected final List<FunctionTool> tools;

    protected AbstractSkill() {
        this.tools = new ArrayList<>();
        this.tools.addAll(new MethodToolProvider(this).getTools());

        this.metadata = new SkillMetadata(this.name(), this.description());
    }

    @Override
    public SkillMetadata metadata() {
        return metadata;
    }

    @Override
    public Collection<FunctionTool> getTools() {
        return tools;
    }
}

3、构建方式对比

特性SkillDesc 申明式Skill 接口实现
构建风格函数式 / 链式调用面向对象 / 显式实现
控制力完整控制 (通过 Lambda)完整控制 (通过 Override)
代码组织集中在一个代码块中分散在独立的类文件中
依赖注入需手动从上下文获取可以通过容器注入
推荐用途快速定义、局部动态逻辑复杂业务领域、深度工程化项目

无论选择哪种方式,你都可以通过 prompt 参数感知输入,通过 onAttach 拦截生命周期,以及通过指令染色控制模型行为。选择的关键在于你希望如何组织你的代码结构。