4.2.function call 函数调用
大模型在面对实时性问题、私域知识型问题或数学计算等问题时可能效果不佳。
您可以使用function call功能,通过调用外部工具来提升模型的输出效果。您可以在调用大模型时,通过tools参数传入工具的名称、描述、入参等信息。
4.2.1.Function Call 流程
Function Call的工作流程示意图如下所示:
4.2.2.工具类
这里的一些工具类是一些测试工具类, 只是模拟对应的功能
获取天气
public class GetWhetherTool {
private String location;
public GetWhetherTool(String location) {
this.location = location;
}
public String call() {
return location + "今天是晴天";
}
}
获取时间
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class GetTimeTool {
public GetTimeTool() {
}
public String call() {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String currentTime = "当前时间:" + now.format(formatter) + "。";
return currentTime;
}
}
获取好友姓名
public class GetNamesTool {
public GetNamesTool() {
}
public String call() {
return "[\"王小二\",\"李小三\",\"赵小四\"]";
}
}
4.2.3.调用处理
通过 通义大模型 进行函数调用
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.tools.FunctionDefinition;
import com.alibaba.dashscope.tools.ToolCallBase;
import com.alibaba.dashscope.tools.ToolCallFunction;
import com.alibaba.dashscope.tools.ToolFunction;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.*;
import com.yuan.tongyibase.tools.GetNamesTool;
import com.yuan.tongyibase.tools.GetTimeTool;
import com.yuan.tongyibase.tools.GetWhetherTool;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.alibaba.dashscope.aigc.generation.GenerationOutput.Choice;
@RestController
@RequestMapping("/tongyi")
public class CallFuncController {
@Value("${tongyi.api-key}")
private String apiKey;
@RequestMapping("/call/func")
public String callFunc(@RequestParam(value = "message", required = false, defaultValue = "中国的首都是哪里?") String message) throws NoApiKeyException, InputRequiredException {
String selectTool = selectTool(message);
return selectTool;
}
public String selectTool(String message) throws NoApiKeyException, ApiException, InputRequiredException {
// 设置API密钥
Constants.apiKey = apiKey;
// 创建SchemaGeneratorConfigBuilder实例,指定使用JSON格式的模式版本
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
// 构建SchemaGeneratorConfig配置,包含额外的OpenAPI格式值,但不使用枚举的toString方法进行展平
SchemaGeneratorConfig config = configBuilder.with(Option.EXTRA_OPEN_API_FORMAT_VALUES)
.without(Option.FLATTENED_ENUMS_FROM_TOSTRING)
.build();
// 根据配置创建SchemaGenerator实例,用于生成模式
SchemaGenerator generator = new SchemaGenerator(config);
// 生成GetWhetherTool类的JSON Schema
ObjectNode jsonSchema_whether = generator.generateSchema(GetWhetherTool.class);
// 生成GetTimeTool类的JSON Schema
ObjectNode jsonSchema_time = generator.generateSchema(GetTimeTool.class);
// 生成GetNamesTool类的JSON Schema
ObjectNode jsonSchema_names = generator.generateSchema(GetNamesTool.class);
// 构建获取指定地区天气的函数定义
FunctionDefinition fd_whether = FunctionDefinition.builder()
.name("get_current_whether") // 设置函数名称
.description("获取指定地区的天气") // 设置函数描述
.parameters(JsonUtils.parseString(jsonSchema_whether.toString()).getAsJsonObject()) // 设置函数参数
.build();
// 构建获取当前时刻时间的函数定义
FunctionDefinition fd_time = FunctionDefinition.builder()
.name("get_current_time") // 设置函数名称
.description("获取当前时刻的时间") // 设置函数描述
.parameters(JsonUtils.parseString(jsonSchema_time.toString()).getAsJsonObject()) // 设置函数参数
.build();
// 构建获取当前names的函数定义
FunctionDefinition fd_names = FunctionDefinition.builder()
.name("get_current_names") // 设置函数名称
.description("获取好友") // 设置函数描述
.parameters(JsonUtils.parseString(jsonSchema_names.toString()).getAsJsonObject()) // 设置函数参数
.build();
// 构建系统消息,用于提示助手使用工具回答问题
Message systemMsg = Message.builder()
.role(Role.SYSTEM.getValue()) // 设置消息角色为系统
.content("你是一个乐于助人的AI助手。当被问到问题时,尽可能使用工具。") // 设置消息内容
.build();
Message userMsg =
Message.builder().role(Role.USER.getValue()).content(message).build();
List<Message> messages = new ArrayList<>();
messages.addAll(Arrays.asList(systemMsg, userMsg));
// 构建生成参数对象,用于配置文本生成的相关参数
GenerationParam param = GenerationParam.builder()
// 设置所使用的模型为“qwen-max”
.model("qwen-turbo") //qwen-max
// 设置对话历史,用于模型生成时参考
.messages(messages)
// 设置生成结果的格式为消息格式
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
// 设置可用的工具函数列表,包括天气查询和时间查询功能
.tools(Arrays.asList(
ToolFunction.builder().function(fd_whether).build(),
ToolFunction.builder().function(fd_time).build(),
ToolFunction.builder().function(fd_names).build()))
.build();
// 大模型的第一轮调用
Generation gen = new Generation();
GenerationResult result = gen.call(param);
System.out.println("\n大模型第一轮输出信息:" + JsonUtils.toJson(result));
for (Choice choice : result.getOutput().getChoices()) {
messages.add(choice.getMessage());
// 如果需要调用工具
if (result.getOutput().getChoices().get(0).getMessage().getToolCalls() != null) {
for (ToolCallBase toolCall : result.getOutput().getChoices().get(0).getMessage().getToolCalls()) {
if (toolCall.getType().equals("function")) {
// 获取工具函数名称和入参
String functionName = ((ToolCallFunction) toolCall).getFunction().getName();
String functionArgument = ((ToolCallFunction) toolCall).getFunction().getArguments();
// 大模型判断调用天气查询工具的情况
if (functionName.equals("get_current_whether")) {
GetWhetherTool GetWhetherFunction = JsonUtils.fromJson(functionArgument, GetWhetherTool.class);
String whether = GetWhetherFunction.call();
Message toolResultMessage = Message.builder()
.role("tool")
.content(String.valueOf(whether))
.toolCallId(toolCall.getId())
.build();
messages.add(toolResultMessage);
System.out.println("\n工具输出信息:" + whether);
}
// 大模型判断调用时间查询工具的情况
else if (functionName.equals("get_current_time")) {
GetTimeTool GetTimeFunction =
JsonUtils.fromJson(functionArgument, GetTimeTool.class);
String time = GetTimeFunction.call();
Message toolResultMessage = Message.builder()
.role("tool")
.content(String.valueOf(time))
.toolCallId(toolCall.getId())
.build();
messages.add(toolResultMessage);
System.out.println("\n工具输出信息:" + time);
}
// 大模型判断调用[ names ]的情况
else if (functionName.equals("get_current_names")) {
GetNamesTool GetNamesFunction =
JsonUtils.fromJson(functionArgument, GetNamesTool.class);
String names = GetNamesFunction.call();
Message toolResultMessage = Message.builder()
.role("tool")
.content(String.valueOf(names))
.toolCallId(toolCall.getId())
.build();
messages.add(toolResultMessage);
System.out.println("\n工具输出信息:" + names);
}
}
}
}
// 如果无需调用工具,直接输出大模型的回复
else {
System.out.println("\n最终答案:" + result.getOutput().getChoices().get(0).getMessage().getContent());
return "\n最终答案:" + result.getOutput().getChoices().get(0).getMessage().getContent();
}
}
// 大模型的第二轮调用 包含工具输出信息
param.setMessages(messages);
result = gen.call(param);
System.out.println("\n大模型第二轮输出信息:" + JsonUtils.toJson(result));
System.out.println(("\n最终答案:" + result.getOutput().getChoices().get(0).getMessage().getContent()));
return "\n最终答案:" + result.getOutput().getChoices().get(0).getMessage().getContent();
}
}
4.2.4.测试
###
GET http://localhost:8081/tongyi/call/func?message=好友的名字是什么?