最新SpringAI-1.1.2接入openai兼容模型

背景

测试了下SpringAI,文档繁琐,一团乱麻,搞了好久终于搞定了,因此本次文档分享一下如何快速接入。

目标

接入openai-兼容的LLM,且官方没有提供原始实现的,如:qianfan,等

版本:

  • springboot: 3.5.9
  • springai: 1.1.2

示例

本次以qianfan为例子,自行在idea创建一个java项目,包管理使用gradle(如果是maven自行把gradle转换成maven就行,用llm转很快)

1. 配置文件gradle

  1. 要引入spring-boot-starter-webflux,用于返回流式内容
  2. 引入openai包,spring-ai-starter-model-openai
gradle 复制代码
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.9'
    id 'io.spring.dependency-management' version '1.1.7'
}

group = 'ai-learn'
version = '0.0.1-SNAPSHOT'
description = 'springai-test'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

repositories {
    mavenCentral()
}

ext {
    set('springAiVersion', "1.1.2")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.ai:spring-ai-starter-model-openai'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.ai:spring-ai-bom:${springAiVersion}"
    }
}

tasks.named('test') {
    useJUnitPlatform()
}

2. yml配置

参考如下

yml 复制代码
server:
  port: 8080
spring:
  ai:
    openai:
      # 模型的基础地址
      base-url: https://qianfan.baidubce.com/v2
      api-key: 你的api-key
      chat:
        options:
          # 模型名
          model: ernie-4.5-turbo-32k
        completions-path: /chat/completions

注意:

  1. 模型基础地址,观察对应模型的说明文档,一般都会给出openai兼容的地址,如千帆OpenAI SDK兼容介绍。
  2. 一些其他坑人情况,如:completions-path,这个具体配置成什么,可以参考说明文档,如:千帆AI应用开发者中心-API参考:模型:文本生成,其中写道:请求地址拼接出来应该是:https://qianfan.baidubce.com/v2/chat/completions
    我们观察springai官方文档说的是:

spring.ai.openai.chat.completions-path(附加到基础 URL 的路径)

默认值:/v1/chat/completions

和我们api中看到的/v2/chat/completions不一致,因此,这里要修改配置completions-path为/chat/completions,大功告成。

3. 新增测试Controller

可以测试三种情况,阻塞,转json,流式

java 复制代码
package ailearn.springaitest;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是一个友好的 AI 助手,请用中文简洁地回答问题。")
                .build();
    }

    // 1. 最简单的文本对话
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }

    // 2. 结构化输出 (Structured Output) - 返回 Java Bean
    // Spring AI 会自动提示 LLM 输出 JSON 并解析
    @GetMapping("/actor")
    public ActorInfo getActorInfo(@RequestParam String name) {
        return chatClient.prompt()
                .user(u -> u
                        .text("请生成一个关于演员 '{actor}' 的简介")
                        .param("actor", name))
                .call()
                .entity(ActorInfo.class); // 自动映射到 Record
    }

    // 3. 流式响应 (Streaming)
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .stream()
                .content()
                // 【关键】打印每一个 Token,观察控制台
                .doOnNext(token -> System.out.println("收到 Token: " + token))
                .doOnComplete(() -> System.out.println("流结束"));
    }

    // 用于结构化输出的 Record
    record ActorInfo(String name, String famousMovie, int age) {}
}

4. 测试

在前端页面分别调用即可,注意流式测试时,输入内容可以是:"帮我生成1个500字的故事",可以观察是否是真流式

附录

其他参数可以自行查看SpringAI官方文档:SpringAI

相关推荐
蜗牛互联网16 小时前
Java标准HTTP调用多模态模型的票据抽取前置校验
java·人工智能·python·http
悟乙己16 小时前
AFAC2026 赛题四金融长文本 Agent竞赛学习笔记:赛题解析(一)
笔记·学习·金融
工具派17 小时前
Agent 不是玄学:用 Java 状态机思维理解 LangGraph
java·人工智能·python
霍霍的袁17 小时前
【C++】哈希表实现指南:从原理到开放定址法、链地址法的实现
c++·学习·哈希算法·散列表·visual studio
十年Java程序媛17 小时前
String / StringBuilder / StringBuffer|别再乱用字符串拼接,底层原理一次讲透
java·spring boot·后端
傲世仙尊17 小时前
类和对象中-构造函数与析构函数把初始化清理自动化
java·运维·自动化
她的男孩17 小时前
权限通了、列表有数,一点分页就炸Forge Admin 用 COUNT(1) 扛住复杂待办
java·spring boot·mybatis
骑着蜗牛撵大象32717 小时前
豆包 Seed 驱动的记账智能体:从拍票抽取到月报生成的全链路落地记录
java·前端·javascript·结构化输出·豆包seed·多模态识别·记账系统
PC2005-cloud17 小时前
MySQL学习笔记:GTID 主从复制,从原理到实践,告别传统 Binlog+Position 的痛点
笔记·学习·mysql
憧憬成为java架构高手的小白17 小时前
langchain4j手敲理解
java·langchain