Spring AI: Make Spring Great Again!设计并实现一款智能Chat Bot!

什么是Spring AI

Spring AI 是 Spring 框架的新项目,旨在简化将生成式 AI 集成到 Java 应用程序中的过程。这个项目通过提供与主流 AI 提供商(如 OpenAI、Anthropic 和 Azure AI)兼容的 API 和抽象层,帮助开发人员在 Spring Boot 应用中利用 AI 功能。开发者可以使用 Spring AI 轻松地调用各种生成式 AI 功能,如文本生成、图像生成、语音识别和嵌入模型等。Spring AI 还支持向量数据库(如 MongoDB Atlas 和 Pinecone),适合构建基于 AI 的推荐系统或搜索功能。

Spring AI 的主要组件包括 AiClient 接口和 Prompt 类,帮助开发者更灵活地管理提示和生成输出。该框架还允许开发者利用 Spring Boot 的自动配置功能来集成这些 AI 功能,使构建和管理生成式 AI 应用变得更便捷。

Quick Start

创建一个SpringBoot工程

创建完成一个 Spring Boot 工程之后,导入 Spring AI 相关依赖项:

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.5</version>
</parent>

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>io.springboot.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>1.0.3</version>
    </dependency>
</dependencies>

因为 Spring AI 需要使用 JDK 17,所以你需要升级一下 JDK 版本。演示的项目使用了 Spring Boot 3.x 版本。

申请 API Key

笔者使用的是 hunyuan 大模型,通过 hunyuan 大模型调用 OpenAI 的接口。直接使用 OpenAI 的接口比较麻烦,所以还是选择了这款大模型。

编写配置项

一切就绪之后,编写一下 application.yml 文件。需要将 spring.ai.openai.base-url 配置项替换为腾讯云混元大模型的地址,api-key 替换为你的申请的 api-key

yaml 复制代码
server:
  port: 8080
  
spring:
  application:
    name: spring-service-demo
  ai:
    openai:
      base-url: https://api.hunyuan.cloud.tencent.com
      api-key: your api key
      chat:
        options:
          model: hunyuan-functioncall

开发一个接口

上面的准备工作准备完成之后,现在就可以聚焦于业务代码的编写了。现在来编写第一个调用大模型的接口的 Controller

java 复制代码
package org.codeart.ai.controller;

import org.springframework.ai.openai.OpenAiChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/ai")
public class AIController {

    private final OpenAiChatClient chatClient;

    @Autowired
    public AIController(OpenAiChatClient chatClient) {
        this.chatClient = chatClient;
    }

    @GetMapping("/generate")
    public ResponseEntity<String> generate(@RequestParam(value = "msg", defaultValue = "Tell me a joke") String msg) {
        String ans = chatClient.call(msg);
        return ResponseEntity.ok(ans);
    }
}

启动应用程序,打开浏览器访问接口可以看到服务器成功地返回了一则笑话:

这说明调用大模型接口已经成功了。

开发一个简单的聊天UI

为了优化一下用户使用体验,可以考虑开发一个简单的交互界面。这里使用 Vue3 + Naive UI 搭建了一个简单的界面。

安装 Naive UI 和 axios:

bash 复制代码
pnpm i -D naive-ui
bash 复制代码
pnpm i axios

修改 App.vue 文件:

ts 复制代码
<template>
  <n-layout class="layout-container">
    <n-card style="width: 1200px">
      <div style="height: 600px; overflow-y: auto; border: 1px solid #ddd; padding: 10px;">
        <div v-for="(message, index) in messages" :key="index" :class="{
          'user-msg': message.role === 'user',
          'bot-msg': message.role === 'bot'
        }">
          <n-space>
            <n-avatar v-if="message.role === 'bot'" round>{{ botAvatar }}</n-avatar>
            <div class="message-content">{{ message.content }}</div>
            <n-avatar v-if="message.role === 'user'" round>{{ userAvatar }}</n-avatar>
          </n-space>
        </div>
      </div>
      <n-space vertical style="margin-top: 10px;">
        <n-input
          v-model:value="input"
          placeholder="输入消息..."
          @keyup.enter="sendMessage"
          :disabled="loading"
        />
        <n-button @click="sendMessage" :disabled="loading">发送</n-button>
      </n-space>
    </n-card>
  </n-layout>
</template>

<script lang="ts" setup>
import { ref } from 'vue'
import { NAvatar, NButton, NCard, NInput, NLayout, NSpace } from 'naive-ui'
import axios from 'axios'

const messages = ref<{ role: string; content: string }[]>([])
const input = ref('')
const loading = ref(false)
const userAvatar = '👤'
const botAvatar = '🤖'
const CHAT_API = 'http://localhost:8080/ai/generate'

const sendMessage = async () => {
  if (!input.value || loading.value) return
  const userMessage = { role: 'user', content: input.value }
  messages.value.push(userMessage)
  input.value = ''

  loading.value = true

  try {
    const response = await axios.get(CHAT_API, {
      params: {
        msg: userMessage.content,
      },
    })
    const botMessage = { role: 'bot', content: response.data }
    messages.value.push(botMessage)
  } catch (error) {
    messages.value.push({ role: 'bot', content: '抱歉,获取回复失败,请稍后再试。' })
  } finally {
    loading.value = false
  }
}
</script>

<style scoped>
.user-msg {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}

.bot-msg {
  display: flex;
  justify-content: flex-start;
  margin-top: 10px;
}

.message-content {
  display: inline-block;
  padding: 10px;
  border-radius: 8px;
  background-color: #f0f0f0;
  max-width: 70%;
  min-width: 125px;
}

.layout-container {
  display: flex;
  justify-content: center;
  height: 100vh;
  margin-top: 50px;
}
</style>

记得在后端工程内部配置一下 CORS,不然浏览器会报跨域访问的错误:

java 复制代码
package org.codeart.ai.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CORSConfig implements WebMvcConfigurer {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // 允许的域
        config.addAllowedOrigin("http://localhost:5173");
        // 允许发送cookie信息
        config.setAllowCredentials(true);
        // 设置被允许的请求类型
        config.addAllowedMethod("*");
        // 设置被允许的头信息
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
        configurationSource.registerCorsConfiguration("/**", config);
        return new CorsFilter(configurationSource);
    }
}

演示效果如下:

相关推荐
胡耀超2 分钟前
我们如何写好提示词、发挥LLM能力、写作指南:从认知分析到动态构建的思维方法
人工智能·python·学习·大模型·llm·提示词·八要素思维
倔强青铜三10 分钟前
Python的Lambda,是神来之笔?还是语法毒瘤?
人工智能·后端·python
a cool fish(无名)10 分钟前
rust-方法语法
开发语言·后端·rust
随意石光1 小时前
秒杀功能、高并发系统关注的问题、秒杀系统设计
后端
随意石光1 小时前
Spring Cloud Alibaba Seata、本地事务、分布式事务、CAP 定理与 BASE 理论、Linux 安装 Seata、Seata的使用
后端
程序员清风1 小时前
程序员入职公司实习后应该学什么?
java·后端·面试
智慧源点1 小时前
基于DataX的数据同步实战
后端
随意石光1 小时前
Java操作Excel报表,EasyExcel用法大全
后端
大葱白菜1 小时前
Java 反射的作用详解:为什么说它是 Java 中最强大的特性之一?
java·后端·程序员
aircrushin1 小时前
在 Expo 中实现 Azure SMS-OTP 登录
前端·javascript·后端