# Spring AI

作者:小傅哥
博客:https://bugstack.cn (opens new window)

沉淀、分享、成长,让自己和他人都能有所收获!😄

大家好,我是技术UP主小傅哥。今天来带大家体验一下 Spring AI 的 Easy 开发!

现在的 OpenAI 对接开发真的是越来越容易了,直接引入 Spring AI 这个组件包,配置上可调用的 ApiHost、ApiKey 就可以完成对 OpenAI(ChatGPT)接口的调用。在此之前也有那么多各类的接口服务,还没有像 OpenAI 这样,被 Spring 如此重视,直接提供专属的 SDK 包,封装所有大模型(暂无国内的)。这也说明 OpenAI 的趋势力量多么庞大!

OpenAI 结合业务才更有价值

虽然 OpenAI 的接口对接是越来越简单了,但我们要知道,一个实际的项目就不只是对接 OpenAI 接口,还需要完成一系列的业务流程封装。就比如目前我们看到国内的各类套壳或自研类的 OpenAI 服务,都会包括;用户鉴权账户支付场景等,一套东西来支撑整个流程跑通,同时还有系统工程背后的舆情监控敏感词过滤数据存储行为分析等各类操作。这些东西也就是常说的,不能只会一个技术点,而是要结合场景,用技术支撑业务落地。

接下来,小傅哥会分享 Spring AI + Gpt-4o 的对接使用,以及介绍如何开发应用级的 OpenAI 项目。

# 一、简单介绍

Spring AI 项目,是为开发 AI 应用程序提供了 Spring 友好的 API 和抽象。所有的大模型对接(OpenAI、Ollama、Azure OpenAI、Amazon Bedrock、HuggingFace、Google VertexAI、Mistral AI)都以一种统一标准的形式进行。这样就减少了大家再额外开发对接的成本了,也不用维护和兼容各个 OpenAI 的接口迭代。

# 二、工程对接

# 1. 工程说明

在此工程中完成了 SpringAI 模块的引入,以及使用提供的 API 接口,完成同步应答、流式应答、图片绘制功能。

# 2. 引入模块

 <dependency>
     <groupId>org.springframework.ai</groupId>
     <artifactId>spring-ai-bom</artifactId>
     <version>0.8.0</version>
     <type>pom</type>
     <scope>import</scope>
 </dependency>
 
 <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
  • spring-ai 也在不断的发展,2024年5月22日 发布了 1.0.0 M1 并对代码做了一些调整。在生产级别使用需要注意📢版本的迭代。

# 2. 接口对接

测试之前需要在 application-dev.yml 配置对接的信息。

spring:
  ai:
    openai:
      base-url: https://api.aws-*****/
      api-key: sk-oLkakcax33mJl628D3A533Fd67A24602Ac37D6*****
1
2
3
4
5

注意 spring-ai 提供了各类大模型的对接,都可以按需配置到这里。在它的源码 autoConfig 类里会有要配置参数的名称,可以参考。

# 2.1 功能测试

@Resource
private OpenAiChatClient chatClient;
@Resource
private OpenAiImageClient openaiImageClient;

@Test
public void test_generate() {
    ChatResponse chatResponse = chatClient.call(
            new Prompt(
                    "1+1",
                    OpenAiChatOptions.builder()
                            .withModel("gpt-4o")
                            .build()
            ));
    log.info("测试结果:{}", JSON.toJSONString(chatResponse));
}

@Test
public void test_generate_stream() throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(1);
    Flux<ChatResponse> stream = chatClient.stream(new Prompt("1+1"));
    stream.subscribe(
            chatResponse -> {
                AssistantMessage output = chatResponse.getResult().getOutput();
                log.info("测试结果: {}", JSON.toJSONString(output));
            },
            Throwable::printStackTrace,
            () -> {
                countDownLatch.countDown();
                System.out.println("Stream completed");
            }
    );
    countDownLatch.await();
}

@Test
public void test_generate_image() {
    ImageResponse imageResponse = openaiImageClient.call(
            new ImagePrompt("画个小狗",
                    OpenAiImageOptions.builder()
                            .withModel("dall-e-3")
                            .withQuality("hd")
                            .withN(1)
                            .withHeight(1024)
                            .withWidth(1024)
                            .build()
            )
    );
    log.info("测试结果: {}", JSON.toJSONString(imageResponse));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

测试结果

24-05-26.16:46:46.287 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.288 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"1","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"+","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"1","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":" equals","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":" ","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.290 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"2","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
24-05-26.16:46:46.290 [ForkJoinPool.commonPool-worker-1] INFO  OpenAITest             - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}}
Stream completed
1
2
3
4
5
6
7
8
9
10
  • 测试结果为 test_generate_stream 流式返回 1+1 的应答结果。

# 2.2 接口服务

@RestController()
@CrossOrigin("*")
@RequestMapping("/api/v1/openai/")
public class OpenAiController {

    @Resource
    private OpenAiChatClient chatClient;

    /**
     * curl http://localhost:8090/api/v1/openai/generate?message=1+1
     */
    @RequestMapping(value = "generate", method = RequestMethod.GET)
    public ChatResponse generate(@RequestParam String message) {
        return chatClient.call(
                new Prompt(
                        message,
                        OpenAiChatOptions.builder()
                                .withModel("gpt-4o")
                                .build()
                ));
    }

    /**
     * curl http://localhost:8090/api/v1/openai/generate_stream?message=1+1
     */
    @RequestMapping(value = "generate_stream", method = RequestMethod.GET)
    public Flux<ChatResponse> generateStream(@RequestParam String message) {
        return chatClient.stream(new Prompt(message));
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

在 OpenAiController 中提供了对接两个接口的方法,以及同步和流式返回。流式返回还可以使用 ResponseBodyEmitter 进行流式返回

Spring AI 提供的 OpenAI 大模型对接是 Easy 的很,减去了自己开发代码对接大模型。但仅仅是这样一个简单还不够,我们还需要学习积累的更多!如;微信鉴权登录怎么做、支付怎么对接、账户额度怎么扣减、限流熔断怎么操作等等,实际场景问题才是有价值的核心内容,所以,咱们要上个大菜!☞ OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 (opens new window)