【AI】✈️实现一个简单的问答服务
目录
👋前言
小伙伴们大家好,前段时间了解了死锁在 java 中的本地模拟,以及如何查询死锁现象,文章链接如下:
【多线程】⭐️(java)本地模拟死锁的产生和排查方式-CSDN博客
平时开发中使用较多的工具,除了开发工具就属 AI 了,一些棘手的问题可以通过 ai 快速处理,目前市场上的服务提供有很多,本人也用过很多,像 通义、kimi 等效果都很不错;最近在别的渠道上看到有一些免费试用的服务提供商,于是在本地通过简单的项目试了下(没有想白嫖):
🍸一、项目搭建
开发环境:JDK8 , 框架:SpringBoot ,实现方式:调用第三方 api
本地使用 IDEA 创建了一个简单的 SpringBoot 项目,通过引入 三方 sdk 方式实现接口,基本环境搭建好之后的操作如下:
1.1 服务注册
因为使用的是智普提供的服务,所以要先注册号账号,获取到 API key 用于后面的接口调用,官网如下:
注册成功之后,进入个人中心创建一个 API key,生成好的值是处理后的,点击后右侧会有一个复制按钮,直接复制
1.2 依赖引入
在创建好的可运行的 springboot 项目中,引入以下内容,刷新 maven 自动下载依赖:
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.3.0</version>
</dependency>
调用工具类代码如下,首先创建一个客户端,传入的参数就是我们在官网上创建的 api key,然后指定了一些超时时间配置,调用时直接通过客户端的 invokeModelApi() 方法即可,这里请求参数除了接口传入的 问题,还有一个uuid创建的唯一标识:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhipu.oapi.ClientV4;
import com.zhipu.oapi.Constants;
import com.zhipu.oapi.service.v4.model.*;
import io.reactivex.Flowable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author HuangBenben
*/
public class AiUtil {
private static final ClientV4 client = new ClientV4
.Builder("51c******gco")
.networkConfig(1000 * 10, 1000 * 10, 1000 * 20, 1000 * 20, TimeUnit.MILLISECONDS)
.build();
private static final ObjectMapper mapper = new ObjectMapper();
/**
* 同步调用
*/
public static String invoke(String question) {
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), question);
messages.add(chatMessage);
String requestId = UUID.randomUUID().toString();
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.FALSE)
.invokeMethod(Constants.invokeMethod)
.messages(messages)
.requestId(requestId)
.build();
ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
try {
return mapper.writeValueAsString(invokeModelApiResp);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return "";
}
}
🍻二、测试
2.1 同步调用
创建一个简单的测试接口,供我们调用并传递所问的问题,本地使用了 coolRequest 插件,点击接口左边的黄色按钮,会快速生成一个调用地址,配置好参数后,结果如下,转换一下 json 格式,结果如下,调用成功:
2.2 流式输出
改写下调用代码,如下:
public static void sseInvoke(String msg) {
// 创建一个消息列表,将用户消息添加进去
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), msg);
messages.add(chatMessage);
String requestId = UUID.randomUUID().toString();
// 构建请求对象,配置为流式请求
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4) // 设置使用的模型
.stream(Boolean.TRUE) // 启用流式返回
.messages(messages) // 设置消息
.requestId(requestId) // 设置请求ID
.build();
// 调用 API 获取流式响应
ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
// 判断 API 请求是否成功
if (sseModelApiResp.isSuccess()) {
// 使用 AtomicBoolean 来控制第一次输出
AtomicBoolean isFirst = new AtomicBoolean(true);
// 将流数据映射到累加器(即接收器)
ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable(), chatMessage)
.doOnNext(accumulator -> {
// 处理流数据
if (isFirst.getAndSet(false)) {
System.out.print("Response: "); // 打印初始响应
}
// 处理 tool_calls,如果有
if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
try {
String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls());
System.out.println("tool_calls: " + jsonString);
} catch (JsonProcessingException e) {
e.printStackTrace(); // 错误处理
}
}
// 打印返回的具体内容
if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
System.out.print(accumulator.getDelta().getContent());
}
})
.doOnComplete(System.out::println) // 完成时打印
.lastElement() // 获取最后的元素
.blockingGet(); // 阻塞并获取结果
// 创建一个新的 Choice 对象用于返回结果
Choice choice = new Choice();
choice.setFinishReason(chatMessageAccumulator.getChoice().getFinishReason());
choice.setIndex(0L);
choice.setDelta(chatMessageAccumulator.getDelta());
// 将 Choice 放入一个列表中
List<Choice> choices = new ArrayList<>();
choices.add(choice);
// 创建返回的数据对象
ModelData data = new ModelData();
data.setChoices(choices);
data.setUsage(chatMessageAccumulator.getUsage());
data.setId(chatMessageAccumulator.getId());
data.setCreated(chatMessageAccumulator.getCreated());
data.setRequestId(chatCompletionRequest.getRequestId());
// 清空流并设置返回数据
sseModelApiResp.setFlowable(null);
sseModelApiResp.setData(data);
}
}
private static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable, ChatMessage chatMessage) {
return flowable
.map(modelData -> {
// 提取 ModelData 中的必要属性
Delta delta = modelData.getChoices().get(0).getDelta();
Choice choice = modelData.getChoices().get(0);
Usage usage = modelData.getUsage();
String id = modelData.getId();
long created = modelData.getCreated();
// 使用带参数的构造函数创建 ChatMessageAccumulator 对象
ChatMessageAccumulator accumulator = new ChatMessageAccumulator(delta, chatMessage, choice, usage, created, id);
return accumulator;
});
}
测试下,结果如下,本地可以看到是每行依次输出:
🍹三、章末
本地只是简单示范下如何调用,以及处理响应,有时间的话自己可以搭建一个简单的页面,将结果展示在页面上;又或者页面搭建好之后,部署到服务器上(Or 内网穿透下)可以通过公网访问,当然以上也要看个人兴趣。
文章到这里就结束了~
原文地址:https://blog.csdn.net/TM007_/article/details/144328476
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!