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

* 用 `@ToolMapping` 注解方法开发
* 用 `FunctionToolDesc` 动态申明开发
* 用 `FunctionTool` 原始接口定制开发
* 用 `AbsTool` 定制开发（介绍 FunctionTool 和 FunctionToolDesc 之间）

提示：所有工具的定制，也是 MCP Tool 的定制（通用）

#### 方式一： ToolMapping 注解声明方式（比较简洁，一个类里可有多个函数工具）


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

```java
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 "..."; 
    }

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

应用参考：

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


#### 方式二： FunctionToolDesc 构建声明方式（适合动态构建）

定制示例：

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

应用参考：

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

#### 方式三： FunctionTool 原始接口实现方式（适合高级定制，一般改用 AbsTool 更简便）


定制示例：

```java
//可以加组件注解（支持注入和拦截）
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 查询合适数据;
    }
}
```

应用参考：

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

#### 方式四： AbsTool 扩展的实现方式（比 FunctionTool 简洁）


定制示例（以上面的 WeatherTool 为例）：

```java
//可以加组件注解（支持注入和拦截）
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、工具的注册（添加）和作用域

* 作为默认工具（是即每次请求时都会附加）。可在语言模型构建时添加。


```java
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());
}
```


* 作为请求工具（当次请求时附加）。和全局工具相比，只是作用域不同。


```java
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());
}
```
