自学内容网 自学内容网

【AI】✈️实现一个简单的问答服务

目录

👋前言

🍸一、项目搭建

        1.1 服务注册

        1.2 依赖引入

🍻二、测试

        2.1 同步调用

        2.2 流式输出

🍹三、章末


👋前言

        小伙伴们大家好,前段时间了解了死锁在 java 中的本地模拟,以及如何查询死锁现象,文章链接如下:

【多线程】⭐️(java)本地模拟死锁的产生和排查方式-CSDN博客

        平时开发中使用较多的工具,除了开发工具就属 AI 了,一些棘手的问题可以通过 ai 快速处理,目前市场上的服务提供有很多,本人也用过很多,像 通义、kimi 等效果都很不错;最近在别的渠道上看到有一些免费试用的服务提供商,于是在本地通过简单的项目试了下(没有想白嫖):

 

🍸一、项目搭建

        开发环境:JDK8 , 框架:SpringBoot  ,实现方式:调用第三方 api

        本地使用 IDEA 创建了一个简单的 SpringBoot 项目,通过引入 三方 sdk 方式实现接口,基本环境搭建好之后的操作如下:

        1.1 服务注册

        因为使用的是智普提供的服务,所以要先注册号账号,获取到 API key 用于后面的接口调用,官网如下:

智谱AI开放平台

        注册成功之后,进入个人中心创建一个 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)!