JDK1.8也可以对接DeepSeek-R1,你知道吗?

什么是ai4j首先,我们先了解一下什么是ai4jAI4J 是一款 Java SDK,用于快速接入 AI 大模型应用。 它能整合多平台大模型,如 OpenAI、Ollama、智谱 Zhipu(ChatGLM)、深度求索 DeepSeek、月之暗面 Moonshot(Kimi)、腾讯混元 Hunyuan、零一万物(01)等,为用户提供快速整合 AI 的能力。 其特点包括提供统一的输入输出(对齐 OpenAI)以消除差异化,优化函数调用(Tool Call)和 RAG 调用,支持向量数据库(如 Pinecone),并且支持 JDK1.8,能满足很多仍在使用 JDK8 版本的应用需求。

什么是ai4j

首先,我们先了解一下什么是ai4j

AI4J 是一款 Java SDK,用于快速接入 AI 大模型应用。它能整合多平台大模型,如 OpenAI、Ollama、智谱 Zhipu(ChatGLM)、深度求索 DeepSeek、月之暗面 Moonshot(Kimi)、腾讯混元 Hunyuan、零一万物(01)等,为用户提供快速整合 AI 的能力。

其特点包括提供统一的输入输出(对齐 OpenAI)以消除差异化,优化函数调用(Tool Call)和 RAG 调用,支持向量数据库(如 Pinecone),并且支持 JDK1.8,能满足很多仍在使用 JDK8 版本的应用需求。

敲重点:JDK1.8

看过上一篇使用SpringAI的都知道,SpringAI对JDK的要求非常高,那次了不起使用了JDK 17,但是Java发展了这么多年,很多项目都是基于JDK1.8来构建的,你让他们现在去升级JDK,可能AI还没接入,项目就先起不来了。

也因此诞生了ai4j,他支持 JDK1.8,能满足很多仍在使用 JDK8 版本的应用需求,并且向量数据库还能帮助很多项目做知识库搜索。

进入正题

我们使用目前最新版本的ai4j

<dependency>
      <groupId>io.github.lnyo-cly</groupId>
      <artifactId>ai4j</artifactId>
      <version>0.8.1</version>
  </dependency>

现在网上很多版本的ai4j都不支持ollama调用,所以直接使用最新版本的话,就没有问题了。

我们依旧是写两个接口,一个直接返回,一个流式返回。

IChatService chatService = aiService.getChatService(PlatformType.OLLAMA);

通过getChatService的方式,选择是用本地ollama还是其他平台。

PS:如果你还不知ollama怎么弄,看这篇《使用SpringAI对接大模型DeepSeek-r1》

它一共支持以下平台。

@AllArgsConstructor
@Getter
public enum PlatformType {
    OPENAI("openai"),
    ZHIPU("zhipu"),
    DEEPSEEK("deepseek"),
    MOONSHOT("moonshot"),
    HUNYUAN("hunyuan"),
    LINGYI("lingyi"),
    OLLAMA("ollama"),
    MINIMAX("minimax"),
    BAICHUAN("baichuan"),
    ;
   ....
}

由于我修改过ollama的端口,所以我没办法使用默认的端口,需要单独设置调用的url

spring.application.name=demo
server.port=8080
ai.ollama.api-host=http://localhost:8000

创建请求体:

// 创建请求参数
  ChatCompletion chatCompletion = ChatCompletion.builder()
         .model("deepseek-r1:7b")
         .message(ChatMessage.withUser(question))
         .build();

直接返回就调用chatCompletion方法:

// 发送chat请求
ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion);

流式放回就调用chatCompletionStream方法:

// 发送chat请求
chatService.chatCompletionStream(chatCompletion, sseListener);

流式的话他是以SSE端点的形式去获取数据,所以需要你实现一个SSE监听器去打印和发送数据给前端。

以下是完整的后端接口:

@RestController
@CrossOrigin
public class OllamChatController {
    // 注入Ai服务
    @Autowired
    private AiService aiService;

    @GetMapping("/chat")
    public String getChatMessage(@RequestParam String question) throws Exception {
        // 获取OLLAMA的聊天服务
        IChatService chatService = aiService.getChatService(PlatformType.OLLAMA);
        // 创建请求参数
        ChatCompletion chatCompletion = ChatCompletion.builder()
               .model("deepseek-r1:7b")
               .message(ChatMessage.withUser(question))
               .build();
        System.out.println(chatCompletion);
        // 发送chat请求
        ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion);
        // 获取聊天内容和token消耗
        String content = chatCompletionResponse.getChoices().get(0).getMessage().getContent();
        long totalTokens = chatCompletionResponse.getUsage().getTotalTokens();
        System.out.println("总token消耗: " + totalTokens);
        return content;
    }

    @GetMapping(path = "/chat-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam String question) {
        Logger logger = LoggerFactory.getLogger(getClass());
        return Flux.create(emitter -> {
            try {
                logger.info("开始进行Chat对话: {}", question);
                // 获取chat服务实例
                IChatService chatService = aiService.getChatService(PlatformType.OLLAMA);
                logger.info("成功创建服务实例");
                // 构造请求参数
                ChatCompletion chatCompletion = ChatCompletion.builder()
                        .model("deepseek-r1:7b")
                        .messages(Arrays.asList(ChatMessage.withUser(question)))
                        .functions()
                        .build();
                logger.info("成功构建流式请求体");
                // 构造监听器
                SseListener sseListener = new SseListener() {
                    @Override
                    protected void send() {
                        try {
                            // 将消息发送到前端
                            String data = this.getCurrStr();
                            if (data != null && !data.isEmpty()) {
                                emitter.next(ServerSentEvent.<String>builder()
                                        .data(data)
                                        .build());
                            }
                        } catch (Exception e) {
                            logger.error("SSE端点报错", e);
                            emitter.error(e);
                        }
                    }
                };
                // 显示函数参数,默认不显示
                sseListener.setShowToolArgs(true);
                // 发送SSE请求
                chatService.chatCompletionStream(chatCompletion, sseListener);
                logger.info("成功请求SSE端点");
            } catch (Exception e) {
                logger.error("流式输出报错", e);
                emitter.error(e);
            }
        });
    }


}

流式的话,我们再写个前端来看看测试效果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat Stream Frontend</title>
</head>

<body>
<input type="text" id="questionInput" placeholder="请输入问题">
<button id="sendButton">发送</button>
<div id="responseContainer"></div>

<script>
    const questionInput = document.getElementById('questionInput');
    const sendButton = document.getElementById('sendButton');
    const responseContainer = document.getElementById('responseContainer');

    sendButton.addEventListener('click', () => {
        const question = questionInput.value;
        if (question.trim() === '') {
            alert('请输入问题');
            return;
        }

        // 创建 EventSource 实例,连接到后端的 SSE 接口
        const eventSource = new EventSource(`http://localhost:8080/chat-stream?question=${encodeURIComponent(question)}`);

        // 监听 message 事件,当接收到服务器发送的消息时触发
        eventSource.onmessage = (event) => {
            const data = event.data;
            // 将接收到的数据追加到响应容器中
            responseContainer.textContent += data;
        };

        // 监听 error 事件,当连接出现错误时触发
        eventSource.onerror = (error) => {
            console.error('EventSource failed:', error);
            // 关闭连接
            eventSource.close();
        };
    });
</script>
</body>

</html>

运行服务,打开html,在输入框输入一个问题,点击按钮发送,在F12的接口请求里,你会在Response里看到服务不断的推送文字给你。

图片图片

相关资讯