一个简单免费的问答系统,人人可学。。。

科技   2024-12-04 11:10   河南  
点击“终码一生”,关注,置顶公众号
每日技术干货,第一时间送达!


1

背景
最近不是很忙,想着做一个简单的Ai问答系统,方便自己使用的同时,也能将自己平时常遇到的问题,做一个归纳整理,当然,这些我希望最后都交给Ai,让他帮我们处理,做干就干。。。
了解了一下国内当前主流的大模型,最后选择了智谱BigModel开发平台提供的大模型。
智谱目前作为国内自研大模型的领先者,现在已经非常成熟,其中GLM-4-Plus已经达到了GPT-4,一些方面甚至已经超越。
而Api调用收费,也非常低,并且还提供了GLM-4-Flash大模型免费调用Api

新用户注册还享有福利,赠送大额Token额度,扫码可直达BigModel开发平台


2

项目搭建
这里搭建了一个简单的web项目,主要是为了方便后面对问题进行归类和统计处理,满足自己的一些特殊需求。
开发环境:JDK17
主要技术:SpringMVC、Mybatis


3

调用GLM-4-Flash模型 Api
  • 模型介绍:https://bigmodel.cn/dev/howuse/glm-4
  • Api文档:https://bigmodel.cn/dev/api/normal-model/glm-4

BigModel开放平台的官方文档,已经非常详细,包括代码Demo,SDK,和参数信息,大家可以参考开发文档,自己调试。本次开发问答系统,主要调用GLM-4-Flash免费大模型Api,个人在调用时,因为环境和包冲突问题,踩到一些坑,后面会放出来,供大家借鉴。

1、引入SDK

<dependency>
    <groupId>cn.bigmodel.openapi</groupId>
    <artifactId>oapi-java-sdk</artifactId>
    <version>release-V4-2.3.0</version>
</dependency>

2、鉴权

鉴权主要有2种形式:ApiKey和Token,我们这里通过Apikey。
  • ApiKey申请:https://bigmodel.cn/usercenter/proj-mgmt/apikeys

3、调用Api

下面是同步调用和流式输出调用的代码Demo,异步调用和自己场景不太符合,就没有测试。大家可以根据需要选择,流式输出相应相对较快,体验也更好,开发处理的东西相对更多一些。

同步调用代码

   /**
     * 同步调用
     */

    public static String invoke(String response) {
        List<ChatMessage> messages = new ArrayList<>();
        ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), response);
        messages.add(chatMessage);
        String requestId = String.format(requestIdTemplate, System.currentTimeMillis());

        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
             // .model("glm-4-flash")
             // .model("glm-4-plus")
                .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 "";
    }

处理效果

流式输出的代码

   /**
     * 流式
     */

    public static String sseInvoke(String msg) {
        List<ChatMessage> messages = new ArrayList<>();
        ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), msg);
        messages.add(chatMessage);
        String requestId = String.format(requestIdTemplate, System.currentTimeMillis());

        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
                .stream(Boolean.TRUE)
                .messages(messages)
                .requestId(requestId)
                .build();
        ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
        if (sseModelApiResp.isSuccess()) {
            AtomicBoolean isFirst = new AtomicBoolean(true);
            ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable(),chatMessage)
                    .doOnNext(accumulator -> {
                        {
                            if (isFirst.getAndSet(false)) {
                                System.out.print("Response: ");
                            }
                            if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
                                String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls());
                                System.out.println("tool_calls: " + jsonString);
                            }
                            if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
                                // 这句代码是返回的具体结果
                                System.out.print(accumulator.getDelta().getContent());
                            }
                        }
                    })
                    .doOnComplete(System.out::println)
                    .lastElement()
                    .blockingGet();

            Choice choice = new Choice();
            choice.setFinishReason(chatMessageAccumulator.getChoice().getFinishReason());
            choice.setIndex(0L);
            choice.setDelta(chatMessageAccumulator.getDelta());

            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);
        }
        return JSON.toJSONString(sseModelApiResp);
    }


    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;
                });
    }

}

处理效果

4、遇到的问题

a、接口相应超时

在测试同步调用时,总是出现响应超时的情况,这里在对请求时间和响应时间做了调整。

private static final ClientV4 client = new ClientV4
            .Builder(API_KEY)
            .networkConfig(1000*20, 1000*20, 1000*20, 1000*20, TimeUnit.MILLISECONDS)
            .build();

b、编译问题

在使用官方提供的代码Demo,进行编译时,多次出现编译错误,排查后,是本地JDK版本过低造成的,做了升级后,不在出现。建议使用JDK17以上版本。

c、包冲突

官方SDK环境中,使用了jackson包,且版本较低,与自己搭建的web项目中有冲突,对其做了移除。

<dependency>
      <groupId>cn.bigmodel.openapi</groupId>
      <artifactId>oapi-java-sdk</artifactId>
      <version>release-V4-2.3.0</version>
      <exclusions>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
        </exclusion>
      </exclusions>
</dependency>
这些问题不一定是共性问题,但遇到的话解决很棘手,供大家借鉴。


4

部署测试

项目只完成了最基本的功能,下面我们通过宝塔部署测试下。

1、登录宝塔

这里用的旧服务器,已经安装过宝塔,部署环境也已经搭建好了,直接创建新项目。也推家大家通过宝塔部署自己的项目,管理起来非常方便。

2、创建项目

选择左侧网站菜单,并「添加Java项目-「独立项目,创建一个新的项目,设置域名和端口号,注意不要冲突。

3、打包上传

项目通过Ftp进行打包,上传,传完解压即可。

4、解析域名

选择A类型,主机记录,指向我们的服务器ip地址

5、运行测试

部署完,直接启动项目,可以看到已经完成了最基本的功能,调用了BigModel的GLM-4-Flash大模型Api,问答界面同样也比较简单,没有过多设计,基本可以满足自己使用。相应时间相对较长,后面会采用流式输出,这个会一点点完善,等到完善起来也会分享给大家。


5

最后
最后,如果你有空闲的时间,和不错的idea,也可以尝试去做下,很多东西都是不经意间做出来的。。。

好了,今天就给大家分享到这里,本工具后台是调用BigModel开放平GLM-4-Flash模型Api,再次感谢BigModel开放平台免费提供这样优秀的大模型,也推荐给大家去测试体验。

  • 模型介绍:https://bigmodel.cn/dev/howuse/glm-4
  • API调用:https://bigmodel.cn/dev/api/normal-model/glm-4


点击「原文阅读」,可以直达BigModel开放平台!

终码一生
开发者聚集地。分享Java相关开发技术(JVM,多线程,高并发,性能调优等),开源项目,常见开发问题和前沿科技资讯!
 最新文章