Solon v3.9.5

chat - 工具(Tool)的四种定制与注册

</> markdown
2026年3月11日 下午5:04:45

1、函数工具的四种定制方式:

  • @ToolMapping 注解方法开发
  • FunctionToolDesc 动态申明开发
  • FunctionTool 原始接口定制开发
  • AbsTool 定制开发(介绍 FunctionTool 和 FunctionToolDesc 之间)

提示:所有工具的定制,也是 MCP Tool 的定制(通用)

方式一: ToolMapping 注解声明方式(比较简洁,一个类里可有多个函数工具)

我们定义个工具类,并设定一个 “天气查询” 函数和 “联网搜索” 的函数

import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.annotation.Param;

//可以加组件注解(支持注入和拦截)
public class Tools {
    //天气查询
    @ToolMapping(description = "获取指定城市的天气情况")
    public String get_weather(@Param(name = "location", description = "根据用户提到的地点推测城市") String location) {
        return "晴,24度"; //可使用 “数据库” 或 “网络” 接口根据 location 查询合适数据;
    }
    
    
    //订单查询
    @ToolMapping(description = "联网查询")
    public String web_search(@Param(name = "query", description = "查询关键字(以空隔隔开)") String query) {
        return "..."; 
    }

    //...//可以加其它注解函数
}

应用参考:

public void case3() throws IOException {
    ChatResponse resp = chatModel
            .prompt("今天杭州的天气情况?")
            .options(o -> o.toolAdd(new MethodToolProvider(new Tools()))   //会自动匹配天气函数
                           .toolAdd(new Tools2())   //也可以省略 MethodToolProvider(内部自动转换)
            .call();
}

方式二: FunctionToolDesc 构建声明方式(适合动态构建)

定制示例:

FunctionToolDesc weatherTool = new FunctionToolDesc("get_weather")
                .description("获取指定城市的天气情况")
                .stringParamAdd("location", "根据用户提到的地点推测城市")
                .doHandle(map -> {
                    return "24度";
                });

应用参考:

public void case3() throws IOException {
    ChatResponse resp = chatModel
            .prompt("今天杭州的天气情况?")
            .options(o -> o.toolsAdd(weatherTool))  //会自动匹配天气函数
            .call();
}

方式三: FunctionTool 原始接口实现方式(适合高级定制,一般改用 AbsTool 更简便)

定制示例:

//可以加组件注解(支持注入和拦截)
public class WeatherTool implements FunctionTool {
    private List<ParamDesc> params = new ArrayList<>();
    
    public WeatherTool() {
        //友好的描述,有助于大模型推测参数值
        params.add(new ParamDesc("location", String.class, true, "根据用户提到的地点推测城市"));
    }

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

    @Override
    public String description() {
        //友好的描述,有助于大模型组织回复消息
        return "获取指定城市的天气情况";
    }
    
    @Override
    public boolean returnDirect() {
        return false;
    }

    @Override
    public ONode inputSchema() {
        return ToolSchemaUtil.buildToolParametersNode(params, new ONode());
    }

    @Override
    public String handle(Map<String, Object> args) {
        String location = (String) args.get("location");

        if(location == null) {
            //大模型有可能会识别失败
            throw new IllegalStateException("arguments location is null (Assistant recognition failure)");
        }

        return "24度";// 可使用 “数据库” 或 “网络” 接口根据 location 查询合适数据;
    }
}

应用参考:

public void case3() throws IOException {
    ChatResponse resp = chatModel
            .prompt("今天杭州的天气情况?")
            .options(o -> o.toolsAdd(new WeatherTool()))  //会自动匹配天气函数
            .call();
}

方式四: AbsTool 扩展的实现方式(比 FunctionTool 简洁)

定制示例(以上面的 WeatherTool 为例):

//可以加组件注解(支持注入和拦截)
public class WeatherTool extends AbsTool {
    public WeatherTool() {
        //友好的描述,有助于大模型推测参数值
        addParam("location", String.class, true, "根据用户提到的地点推测城市"));
    }
    
    @Override
    public String description() {
        //友好的描述,有助于大模型组织回复消息
        return "获取指定城市的天气情况";
    }
   
    @Override
    public String handle(Map<String, Object> args) {
        String location = (String) args.get("location");

        if(location == null) {
            //大模型有可能会识别失败
            throw new IllegalStateException("arguments location is null (Assistant recognition failure)");
        }

        return "24度";// 可使用 “数据库” 或 “网络” 接口根据 location 查询合适数据;
    }
}

2、工具的注册(添加)和作用域

  • 作为默认工具(是即每次请求时都会附加)。可在语言模型构建时添加。
public void case3() throws IOException {
    ChatModel.of("http://127.0.0.1:11434/api/chat")
                .provider("ollama")
                .model("llama3.2")
                .defaultToolAdd(new WeatherTool()) //添加默认工具(即所有请求可用)
                .defaultToolAdd(new WeatherTool2()) //可以添加多套工具(只是示例下)
                .build();
                
                
    ChatResponse resp = chatModel
            .prompt("今天杭州的天气情况?")
            .call();

    //打印消息
    log.info("{}", resp.getMessage());
}
  • 作为请求工具(当次请求时附加)。和全局工具相比,只是作用域不同。
public void case3() throws IOException {
    ChatModel.of("http://127.0.0.1:11434/api/chat")
                .provider("ollama")
                .model("llama3.2")
                .build();
                
                
    ChatResponse resp = chatModel
            .prompt("今天杭州的天气情况?")
            .options(o -> o.toolAdd(new WeatherTool())) //添加请求函数
            .call();

    //打印消息
    log.info("{}", resp.getMessage());
}