You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

125 lines
6.6 KiB

package com.deepchart;
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.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.JsonUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class Main {
// 若使用新加坡地域的模型,请释放下列注释
// static {Constants.baseHttpApiUrl="https://dashscope-intl.aliyuncs.com/api/v1";}
/**
* 第一步:定义工具的本地实现。
* @param arguments 模型传入的、包含工具所需参数的JSON字符串。
* @return 工具执行后的结果字符串。
*/
public static String getCurrentWeather(String arguments) {
try {
// 模型提供的参数是JSON格式的,需要我们手动解析。
ObjectMapper objectMapper = new ObjectMapper();
JsonNode argsNode = objectMapper.readTree(arguments);
String location = argsNode.get("location").asText();
// 用随机结果来模拟真实的API调用或业务逻辑。
List<String> weatherConditions = Arrays.asList("晴天", "多云", "雨天");
String randomWeather = weatherConditions.get(new Random().nextInt(weatherConditions.size()));
return location + "今天是" + randomWeather + "。";
} catch (Exception e) {
// 异常处理,确保程序健壮性。
return "无法解析地点参数。";
}
}
public static void main(String[] args) {
try {
// 第二步:向模型描述(注册)我们的工具。
String weatherParamsSchema =
"{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"城市或县区,比如北京市、杭州市、余杭区等。\"}},\"required\":[\"location\"]}";
FunctionDefinition weatherFunction = FunctionDefinition.builder()
.name("get_current_weather") // 工具的唯一标识名,必须与本地实现对应。
.description("当你想查询指定城市的天气时非常有用。") // 清晰的描述能帮助模型更好地决定何时使用该工具。
.parameters(JsonUtils.parseString(weatherParamsSchema).getAsJsonObject())
.build();
Generation gen = new Generation();
String userInput = "北京天气咋样";
List<Message> messages = new ArrayList<>();
messages.add(Message.builder().role(Role.USER.getValue()).content(userInput).build());
// 第四步:首次调用模型。将用户的请求和我们定义好的工具列表一同发送给模型。
GenerationParam param = GenerationParam.builder()
.model("qwen-plus") // 指定需要调用的模型。
.apiKey("sk-5bf09a590daf408cb1126c2c6684e982") // 从环境变量中获取API Key。
.messages(messages) // 传入当前的对话历史。
.tools(Arrays.asList(ToolFunction.builder().function(weatherFunction).build())) // 传入可用的工具列表。
.resultFormat(GenerationParam.ResultFormat.MESSAGE)
.build();
GenerationResult result = gen.call(param);
Message assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput); // 将模型的首次回复也加入到对话历史中。
// 第五步:检查模型的回复,判断它是否请求调用工具。
if (assistantOutput.getToolCalls() == null || assistantOutput.getToolCalls().isEmpty()) {
// 情况A:模型没有调用工具,而是直接给出了回答。
System.out.println("无需调用天气查询工具,直接回复:" + assistantOutput.getContent());
} else {
// 情况B:模型决定调用工具。
// 使用 while 循环可以处理模型连续调用多次工具的场景。
while (assistantOutput.getToolCalls() != null && !assistantOutput.getToolCalls().isEmpty()) {
ToolCallBase toolCall = assistantOutput.getToolCalls().get(0);
// 从模型的回复中解析出工具调用的具体信息(要调用的函数名、参数)。
ToolCallFunction functionCall = (ToolCallFunction) toolCall;
String funcName = functionCall.getFunction().getName();
String arguments = functionCall.getFunction().getArguments();
System.out.println("正在调用工具 [" + funcName + "],参数:" + arguments);
// 根据工具名,在本地执行对应的Java方法。
String toolResult = getCurrentWeather(arguments);
// 构造一个 role 为 "tool" 的消息,其中包含工具的执行结果。
Message toolMessage = Message.builder()
.role("tool")
.toolCallId(toolCall.getId())
.content(toolResult)
.build();
System.out.println("工具返回:" + toolMessage.getContent());
messages.add(toolMessage); // 将工具的返回结果也加入到对话历史中。
// 第六步:再次调用模型。
param.setMessages(messages);
result = gen.call(param);
assistantOutput = result.getOutput().getChoices().get(0).getMessage();
messages.add(assistantOutput);
}
// 第七步:打印模型经过总结后,生成的最终回复。
System.out.println("助手最终回复:" + assistantOutput.getContent());
}
} catch (NoApiKeyException | InputRequiredException e) {
System.err.println("错误: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
}