chat - 函数应用与定制(Function Call)
Function calling 能够将大语言模型连接到外部的数据和系统。可以通过定义一组函数作为模型可访问的工具,并根据对话历史在适当的时候使用它们。然后在应用端执行这些函数,并将结果反馈给模型。
进而实现最新的数据状态(比如联网查询时实天气)或者指令交互(比如做运维操作)。
是否支持?需要核对模型提供方的说明。
1、Function (本地函数申明)接口与注解
接口需要申明函数的名字,描述,参数列表(每个参数需要有名字、描述、类型),及以处理方法。
public interface ChatFunction {
/**
* 名字
*/
String name();
/**
* 描述
*/
String description();
/**
* 参数
*/
Iterable<ChatFunctionParam> params();
/**
* 处理
*/
String handle(Map<String, Object> args);
}
开发时,也可以使用注解简化函数申明(不需要容器驱动):
注解 | 描述 |
---|---|
@FunctionMapping(name, description) | 函数映射 |
@FunctionParam(name, type, description) | 函数的参数申明 |
2、三种函数定制方式
- (1) 注解申明的定制(比较简洁,一个类里可有多个函数)。示例:
我们定义个工具类,并设定一个 “天气查询” 函数和 “联网搜索” 的函数
//可以加组件注解(支持注入和拦截)
public class Tools {
//天气查询
@FunctionMapping(description = "获取指定城市的天气情况")
public String get_weather(@FunctionParam(name = "location", description = "根据用户提到的地点推测城市") String 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.functionAdd(new Tools())) //会自动匹配天气函数
.call();
}
- (2) 构建申明方式(比较简洁)。示例:
ChatFunctionDecl weatherChatFunction = new ChatFunctionDecl("get_weather")
.description("获取指定城市的天气情况")
.stringParam("location", "根据用户提到的地点推测城市")
.handle(map -> {
return "24度";
});
应用示例:
public void case3() throws IOException {
ChatResponse resp = chatModel
.prompt("今天杭州的天气情况?")
.options(o -> o.functionAdd(weatherChatFunction)) //会自动匹配天气函数
.call();
}
- (3) 接口实现的定制方式(比较原始)。示例:
//可以加组件注解(支持注入和拦截)
public class WeatherChatFunction implements ChatFunction {
private List<ChatFunctionParam> params = new ArrayList<>();
public WeatherChatFunction() {
//友好的描述,有助于大模型推测参数值
params.add(new ChatFunctionParamDecl("location", String.class, "根据用户提到的地点推测城市"));
}
@Override
public String name() {
return "get_weather";
}
@Override
public String description() {
//友好的描述,有助于大模型组织回复消息
return "获取指定城市的天气情况";
}
@Override
public Iterable<ChatFunctionParam> params() {
return params;
}
@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.functionAdd(new WeatherChatFunction())) //会自动匹配天气函数
.call();
}
3、函数的添加和作用域
- 全局函数(是即每次请求时都会附加)。可在语言模型构建时添加。
public void case3() throws IOException {
ChatModel.of("http://127.0.0.1:11434/api/chat")
.provider("ollama")
.model("llama3.2")
.globalFunctionAdd(new WeatherChatFunction()) //添加全局函数
.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.functionAdd(new WeatherChatFunction())) //添加请求函数
.call();
//打印消息
log.info("{}", resp.getMessage());
}