Springboot --- 使用國內的 AI 大模型 對話

codervibe發表於2024-10-12
  • 實在是不知道標題寫什麼了 可以在評論區給個建議哈哈哈哈 先用這個作為標題吧

嘗試使用 國內給出的 AI 大模型做出一個 可以和 AI 對話的 網站出來

  • 使用 智普AI 只能 在控制檯中輸出 對應的資訊 不如就做一個 maven 的 專案呼叫對應的API
    https://open.bigmodel.cn/dev/api#glm-4
    <dependency>
        <groupId>cn.bigmodel.openapi</groupId>
        <artifactId>oapi-java-sdk</artifactId>
        <version>release-V4-2.0.0</version>
    </dependency>
  • 使用 普通的 java -- Maven專案 只能在控制檯 檢視結果 也就是 說沒有辦法在其他平臺 使
    用 製作出來的 AI ChatRobot
  • 思來想去 不如 將這個東西寫成 QQ 機器人
  • 但是因為我找到的 那個 不更新了 或者 騰訊不支援了 讓我放棄了 寫成 QQ 機器人的想法
  • 於是我就嘗試將這個寫成一個本地的 AI 對話機器人 但是 在翻看 官方給出的 Demo 我偶然發現了一個方法 他的 輸出似乎是一個 json 轉換成的 String
  • 這個方法並沒有將這個String 返回出來 而是 直接在控制檯列印
package com.codervibe.utils;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.zhipu.oapi.ClientV4;
import com.zhipu.oapi.Constants;
import com.zhipu.oapi.service.v4.image.CreateImageRequest;
import com.zhipu.oapi.service.v4.image.ImageApiResponse;
import com.zhipu.oapi.service.v4.model.*;
import io.reactivex.Flowable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

public class ChatAPIUtils {
    private static final String API_KEY = "cb11ad7f3b68ce03ed9be6e13573aa19";

    private static final String API_SECRET = "nG7UQrrXqsXtqD1S";

    private static final ClientV4 client = new ClientV4.Builder(API_KEY, API_SECRET).build();

    private static final ObjectMapper mapper = defaultObjectMapper();


    public static ObjectMapper defaultObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
        mapper.addMixIn(ChatFunction.class, ChatFunctionMixIn.class);
        mapper.addMixIn(ChatCompletionRequest.class, ChatCompletionRequestMixIn.class);
        mapper.addMixIn(ChatFunctionCall.class, ChatFunctionCallMixIn.class);
        return mapper;
    }

    // 請自定義自己的業務id
    private static final String requestIdTemplate = "mycompany-%d";



    /**
     * 同步呼叫
     */
    public static String InvokeApi(String content) throws JsonProcessingException {
        List<ChatMessage> messages = new ArrayList<>();
        ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), content);
        messages.add(chatMessage);
        String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
        // 函式呼叫引數構建部分
        List<ChatTool> chatToolList = new ArrayList<>();
        ChatTool chatTool = new ChatTool();
        chatTool.setType(ChatToolType.FUNCTION.value());
        ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
        chatFunctionParameters.setType("object");
        Map<String, Object> properties = new HashMap<>();
        properties.put("location", new HashMap<String, Object>() {{
            put("type", "string");
            put("description", "城市,如:北京");
        }});
        properties.put("unit", new HashMap<String, Object>() {{
            put("type", "string");
            put("enum", new ArrayList<String>() {{
                add("celsius");
                add("fahrenheit");
            }});
        }});
        chatFunctionParameters.setProperties(properties);
        ChatFunction chatFunction = ChatFunction.builder()
                .name("get_weather")
                .description("Get the current weather of a location")
                .parameters(chatFunctionParameters)
                .build();
        chatTool.setFunction(chatFunction);
        chatToolList.add(chatTool);
        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
                .stream(Boolean.FALSE)
                .invokeMethod(Constants.invokeMethod)
                .messages(messages)
                .requestId(requestId)
                .tools(chatToolList)
                .toolChoice("auto")
                .build();
        ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
        try {
        // 這裡返回出去是一個 json 
            return mapper.writeValueAsString(invokeModelApiResp);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return mapper.writeValueAsString(new ModelApiResponse());
    }

    public static void CreateImage(String content) {
        CreateImageRequest createImageRequest = new CreateImageRequest();
        createImageRequest.setModel(Constants.ModelCogView);
        createImageRequest.setPrompt(content);
        ImageApiResponse imageApiResponse = client.createImage(createImageRequest);
        System.out.println("imageApiResponse:" + JSON.toJSONString(imageApiResponse));
    }

}

  • 工具類中 InvokeApi 方法 最後獲得的是一個 ModelApiResponse類 這個類有點類似於 統一返回型別 但是我在這裡 只需要裡面的具體方法 請求狀態和 資訊 並不需要 (有另外一個統一返回型別定義 ) 所以在 後面我將這個方法 修改 改為 將我需要的資料返回給controller
  • 實際上這是不應該直接返回給 controller 的 而是 應該 透過 service 的 因為service中才是真正的業務程式碼
  • 修改後的方法 程式碼如下
    /**
     * 同步呼叫
     */
    public static ModelData InvokeApi(String content) throwsJsonProcessingException{
        List<ChatMessage> messages = new ArrayList<>();
        ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), content);
        messages.add(chatMessage);
        String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
        // 函式呼叫引數構建部分
        List<ChatTool> chatToolList = new ArrayList<>();
        ChatTool chatTool = new ChatTool();
        chatTool.setType(ChatToolType.FUNCTION.value());
        ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
        chatFunctionParameters.setType("object");
        Map<String, Object> properties = new HashMap<>();
        properties.put("location", new HashMap<String, Object>() {{
            put("type", "string");
            put("description", "城市,如:北京");
        }});
        properties.put("unit", new HashMap<String, Object>() {{
            put("type", "string");
            put("enum", new ArrayList<String>() {{
                add("celsius");
                add("fahrenheit");
            }});
        }});
        chatFunctionParameters.setProperties(properties);
        ChatFunction chatFunction = ChatFunction.builder()
                .name("get_weather")
                .description("Get the current weather of a location")
                .parameters(chatFunctionParameters)
                .build();
        chatTool.setFunction(chatFunction);
        chatToolList.add(chatTool);
        ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
                .stream(Boolean.FALSE)
                .invokeMethod(Constants.invokeMethod)
                .messages(messages)
                .requestId(requestId)
                .tools(chatToolList)
                .toolChoice("auto")
                .build();
        ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest);
        ModelData data = invokeModelApiResp.getData();
        return data;
  • 而這裡的資訊實際上是一層層 抽絲剝繭 剝離出來的
    List<Choice> choices = data.getChoices();
    System.out.println("choices.toString() = " + choices.toString());
    for (Choice choice : choices) {
        ChatMessage message = choice.getMessage();
        System.out.println("message.getContent() = " + message.getContent());
        //本來這裡想返回具體的資訊類但是發現 上面的的那個ModelApiResponse類 也是一個 統一返回型別 也包含這 請求狀態碼 之類的定義
        return message;
    }
    return new ChatMessage();
    try {
        return mapper.writeValueAsString(invokeModelApiResp);
    } catch (JsonProcessingException e) {
            e.printStackTrace();
    }
    return mapper.writeValueAsString(new ModelApiResponse());    
  • 可以看到我的這段程式碼 有多個 return 所以這實際上是一段假 程式碼
  • 每一個return 實際上官方都 對應的 model 或者說 resoponse
  • controller 程式碼
    @PostMapping("/chat")
    public R chat(@RequestParam("content") String content) throws JsonProcessingException {
        /**
         * data 中的 choices 是一個 List<Choice> 型別但是實際上只有一個所以索性直接獲取陣列下標0的物件
         */
        logger.info(ChatAPIUtils.InvokeApi(content).getChoices().get(0).getMessage().getContent().toString());
        return R.ok().data("content", ChatAPIUtils.InvokeApi(content));
    }
  • 修改 由 service 層 呼叫 工具類
  • service 程式碼
  • service 介面
package com.codervibe.server.service;

import com.zhipu.oapi.service.v4.image.ImageResult;
import com.zhipu.oapi.service.v4.model.ModelData;

public interface ChatService {
    /**
     * AI 對話
     */
    ModelData AIdialogue(String content);

    /**
     * AI  畫圖
     */
    ImageResult AIcreateimage(String content);
}
  • service 介面實現

package com.codervibe.server.Impl;

import com.codervibe.server.service.ChatService;
import com.codervibe.utils.ChatAPIUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.zhipu.oapi.service.v4.image.ImageResult;
import com.zhipu.oapi.service.v4.model.ModelData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service("chatService")
public class ChatServiceImpl implements ChatService {
    Logger logger = LoggerFactory.getLogger(ChatServiceImpl.class);
    /**
     * AI 對話
     * @param content
     */
    @Override
    public ModelData AIdialogue(String content) {
        logger.info(ChatAPIUtils.InvokeApi(content).getChoices().get(0).getMessage().getContent().toString());
        return ChatAPIUtils.InvokeApi(content);
    }

    /**
     * AI  畫圖
     *
     * @param content
     */
    @Override
    public ImageResult AIcreateimage(String content) {
        logger.info(ChatAPIUtils.CreateImage(content).getData().get(0).getUrl());
        return ChatAPIUtils.CreateImage(content);
    }
}

  • controller 層呼叫 service
****package com.codervibe.web.controller;

import com.codervibe.server.service.ChatService;
import com.codervibe.utils.ChatAPIUtils;
import com.codervibe.web.common.response.R;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;

@RestController
@RequestMapping("/chat")
public class ChatController {
    Logger logger = LoggerFactory.getLogger(ChatController.class);
    @Resource
    private ChatService chatService;
    @PostMapping("/content")
    public R chat(@RequestParam("content") String content) {
        return R.ok().data("content", chatService.AIdialogue(content));
    }
    @PostMapping("/AIcreateimage")
    public R AIcreateimage(@RequestParam("content") String content){
        return R.ok().data("image",chatService.AIcreateimage(content));
    }
}

  • 現在 雖然可以 和 AI 進行對話 但是 資料返回的速度實在是太慢 所以我打算 將 常見的問題和答案 儲存在本地的資料庫中以提升 資料返回的速度 這只是一個初步的想法
  • 最後的想法 還未實現 先這樣
  • 粉絲群 企鵝 179469398

相關文章