基于Spring AI alibaba组件AI问答功能开发示例
基于Spring AI alibaba组件AI问答功能开发示例
功能效果图:
http://localhost:9999/ai/test
http://localhost:9999/ai/chat?input=ai
配置 application.yaml 指定 API-KEY(可通过访问阿里云百炼模型服务平台获取,有免费额度可用。)
阿里云百炼
阿里云百炼模型服务平台
https://ai.aliyun.com/
架构版本
JDK17
SpringBoot3.3.3
Spring-AI 1.0.0-M3
Spring-AI-Alibaba1.0.0-M3.1
vue3
架构版本JDK17
SpringBoot3.3.3
Spring-AI 1.0.0-M3
Spring-AI-Alibaba1.0.0-M3.1
vue3
aiApplication.java入口类
package com.cwgis;
import com.cwgis.pg.function.MockOrderService;
import com.cwgis.pg.function.Response;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Description;
import java.util.function.Function;
/**
* Hello world!
*/
@SpringBootApplication
public class aiApplication {
public static void main(String[] args) {
SpringApplication.run(aiApplication.class, args);
System.out.println("===============");
System.out.println("ai服务正在运行中!");
System.out.println("===============");
}
@Bean
@Description("根据用户编号和订单编号查询订单信息")
public Function<MockOrderService.Request, Response> getOrderFunction(
MockOrderService mockOrderService) {
return mockOrderService::getOrder;
}
}
ChatController.java问答服务类
package com.cwgis;
import com.cwgis.pg.core.AjaxResult;
import com.cwgis.pg.function.MJDLTBService;
import com.cwgis.pg.function.MockWeatherService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Objects;
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@GetMapping("/test")
public AjaxResult test() {
String r= this.chatClient.prompt().user("测试").call().content();
return AjaxResult.success(r);
}
//自动加载自定义function函数
@PostMapping("/chat")
public AjaxResult chat(String input) {
String r= this.chatClient.prompt()
.function("getWeather", "根据城市查询天气", new MockWeatherService())
.functions("getOrderFunction") //获取aiApplication类中注册的Bean函数
.function("MJDLTBService","根据城市和地类名称查询地类图斑面积",new MJDLTBService())
.user(input).call().content();
return AjaxResult.success(r);
}
@PostMapping("/stream")
public AjaxResult stream(String input) {
Flux<String> content = this.chatClient.prompt().user(input).stream().content();
String r= Objects.requireNonNull(content.collectList().block()).stream().reduce((a, b) -> a + b).get();
return AjaxResult.success(r);
}
//函数调用
@PostMapping("/weather-service")
public AjaxResult weatherService(String subject) {
String r= chatClient.prompt()
.function("getWeather", "根据城市查询天气", new MockWeatherService())
.user(subject)
.call()
.content();
return AjaxResult.success(r);
}
@PostMapping("/order-detail")
public AjaxResult orderDetail() {
String r= chatClient.prompt()
.functions("getOrderFunction") //获取aiApplication类中注册的Bean函数
.user("帮我查询一下订单, 用户编号为1001, 订单编号为2001")
.call()
.content();
return AjaxResult.success(r);
}
//
}
MJDLTBService.java自定义服务函数
package com.cwgis.pg.function;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.function.Function;
//采用AI问答模式,从本地数据库中查询地类面积值的功能
public class MJDLTBService implements Function<MJDLTBService.Request, Response> {
@Override
public Response apply(MJDLTBService.Request request) {
String city=request.city();
String dlmc=request.dlmc();
if(city!=null && dlmc!=null)
{
//到数据库中去查询,用sql语句查询
String sql="select sum(tbmj) from dltb where zldwmc like '%"+request.city()+"%' and dlmc like '%"+request.dlmc()+"%'";
//sde.getValueDouble(sql);
return new Response(String.format("%s%s的地类面积为查询后面积值500亩", request.city(),request.dlmc()));
}
else {
return new Response(String.format("暂时无法查询%s的地类面积。", request.city()));
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonClassDescription("根据城市和地类名称查询地类图斑面积")
public record Request(
@JsonProperty(required = true, value = "city") @JsonPropertyDescription("城市, 比如杭州") String city,
@JsonProperty(required = true, value = "dlmc") @JsonPropertyDescription("地类, 比如水田、旱地、耕地、园地、林地、草地、建设用地、农用地、未利用地") String dlmc) {
}
}
MockWeatherService.java自定义天气查询服务
package com.cwgis.pg.function;
import java.util.function.Function;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
public class MockWeatherService implements Function<MockWeatherService.Request, Response> {
@Override
public Response apply(Request request) {
if (request.city().contains("杭州")) {
return new Response(String.format("%s%s晴转多云, 气温32摄氏度。", request.date(), request.city()));
}
else if (request.city().contains("上海")) {
return new Response(String.format("%s%s多云转阴, 气温31摄氏度。", request.date(), request.city()));
}
else if (request.city().contains("成都")) {
return new Response(String.format("%s%s阴转小雨, 气温5摄氏度。", request.date(), request.city()));
}
else {
return new Response(String.format("暂时无法查询%s的天气状况。", request.city()));
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonClassDescription("根据日期和城市查询天气")
public record Request(
@JsonProperty(required = true, value = "city") @JsonPropertyDescription("城市, 比如杭州") String city,
@JsonProperty(required = true, value = "date") @JsonPropertyDescription("日期, 比如2024-08-22") String date) {
}
}
public record Response(String description) {
}
resources/application.yml应用配置参数
#基本配置
server.port: 9999
server.tomcat.max-http-form-post-size: 5120MB
spring:
application:
name: galaxy-ai
ai:
dashscope:
api-key: sk-111111111111111111111111111
pom.xml配置参数
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cwgis</groupId>
<artifactId>galaxy-ai</artifactId>
<version>1.0-SNAPSHOT</version>
<name>galaxy-ai</name>
<description>Building AI applications with Spring Boot</description>
<!-- FIXME change it to the project's website -->
<url>http://www.cwgis.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>3.3.3</spring-boot.version>
<commons.io.version>2.13.0</commons.io.version>
<commons.text.version>1.6</commons.text.version>
<commons.lang3.version>3.12.0</commons.lang3.version>
<commons.fileupload.version>1.3.3</commons.fileupload.version>
<!-- Spring AI -->
<spring-ai.version>1.0.0-M3</spring-ai.version>
<spring-ai-alibaba.version>1.0.0-M3.1</spring-ai-alibaba.version>
<dashscope-sdk-java.version>2.15.1</dashscope-sdk-java.version>
<!-- plugin versions -->
<maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
<maven-failsafe-plugin.version>3.1.2</maven-failsafe-plugin.version>
<maven-javadoc-plugin.version>3.5.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.3.0</maven-source-plugin.version>
<jacoco-maven-plugin.version>0.8.10</jacoco-maven-plugin.version>
<flatten-maven-plugin.version>1.5.0</flatten-maven-plugin.version>
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<asciidoctor-maven-plugin.version>2.2.3</asciidoctor-maven-plugin.version>
<maven-assembly-plugin.version>3.7.0</maven-assembly-plugin.version>
<maven-dependency-plugin.version>3.5.0</maven-dependency-plugin.version>
<maven-site-plugin.version>4.0.0-M13</maven-site-plugin.version>
<maven-project-info-reports-plugin.version>3.4.5</maven-project-info-reports-plugin.version>
<maven-jar-plugin.version>3.3.0</maven-jar-plugin.version>
<spring-javaformat-maven-plugin.version>0.0.39</spring-javaformat-maven-plugin.version>
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons.text.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>common-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--add alibaba java lib -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>${dashscope-sdk-java.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<!-- Optionally: parameterized tests support -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version> <!-- 检查是否有新版本并相应更新 -->
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<groupId>io.spring.javaformat</groupId>
<artifactId>spring-javaformat-maven-plugin</artifactId>
<version>${spring-javaformat-maven-plugin.version}</version>
<executions>
<execution>
<phase>validate</phase>
<inherited>true</inherited>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>${maven-site-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>${java.version}</release>
<compilerArgs>
<compilerArg>-parameters</compilerArg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>${surefireArgLine}</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifestEntries>
<Implementation-Title>${project.artifactId}</Implementation-Title>
<Implementation-Version>${project.version}</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>ossrh</flattenMode>
<pomElements>
<distributionManagement>remove</distributionManagement>
<dependencyManagement>remove</dependencyManagement>
<repositories>remove</repositories>
<scm>keep</scm>
<url>keep</url>
<organization>resolve</organization>
</pomElements>
</configuration>
</execution>
<execution>
<id>clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
ai.ts前端访问代码
import request from "@/utils/request"
export function chat(msg) {
let data={
input:msg
};
//let ai_url=config.ai_url;
//var jsonStr=JSON.stringify(data);
return request({baseURL:"/ai",url:'/ai/chat?input='+msg,method:'post'});
}
export function test() {
// var jsonStr=JSON.stringify(data);
//let ai_url=config.ai_url;
return request({baseURL:"/ai",url:'/ai/test',method:'get'});
}
export function weatherService(msg) {
let data={
input:msg
};
//let ai_url=config.ai_url;
//var jsonStr=JSON.stringify(data);
return request({baseURL:"/ai",url:'/ai/weather-service?subject='+msg,method:'post'});
}
export function orderDetail() {
// var jsonStr=JSON.stringify(data);
//let ai_url=config.ai_url;
return request({baseURL:"/ai",url:'/ai/order-detail',method:'post'});
}
前端index.vue页面
<template>
<div v-loading="loading" class="app" element-loading-text="数据请求中">
<div class="app_content">请在下面输入框提问后搜索</div>
<a-textarea v-model:value="msgAsk" placeholder="Basic usage" :rows="20" />
<a-input-search
v-model:value="msg"
placeholder="请输入要搜索的信息"
enter-button="搜索"
size="large"
@search="searchClick"
/>
</div>
</template>
<script setup lang="ts">
import * as page from "@/api/vuePage"
import * as ai from "@/api/ai"
//
const loading = page.ts_ref(false);
let msg=page.ts_ref("");
let msgAsk=page.ts_ref("");
const searchClick=async function(){
loading.value=true;
//page.showMsg(msg.value);
let r=await ai.chat(msg.value);
msgAsk.value=r.msg;
//console.log(r);
//debugger;
//let r=await ai.orderDetail();
//msgAsk.value=r.msg;
//let r=await ai.weatherService(msg.value);
//msgAsk.value=r.msg;
//
loading.value=false;
}
</script>
<style scoped lang="less">
.app{
border: 1px solid #ccc;
height: 99%;
margin: 0 auto;
width: 1024px;
background-color: white;
box-shadow: 0 0 20px 3px #ccc;
.app_header {
text-align: center;
border-bottom: 1px solid #9a9a9a;
height: 90px;
.app_title {
font-size: 24px;
font-weight: bolder;
height: 90px;
line-height: 90px;
box-sizing: border-box;
color: #2f2f2f;
}
.date {
margin-top: 10px;
font-size: 15px;
height: 40px;
color: #8a8a8a;
background-color: #ebe6eb;
line-height: 40px;
}
}
.app_content {
margin-top: 15px;
min-height: 30px;
padding: 20px;
box-sizing: border-box;
overflow: auto;
height: 12%;
}
}
</style>
本blog地址:https://blog.csdn.net/hsg77