🔥个人主页: 中草药

 🔥专栏:【Java】登神长阶 史诗般的Java成神之路


Spring AI Alibaba

Spring AI Alibaba 官网_快速构建 JAVA AI 应用

Spring AI Alibaba 是阿里巴巴集团在人工智能领域推出的重要技术框架,它将 Spring 生态的工程化优势与阿里云的大模型能力深度结合,为 Java 开发者提供了一站式的 AI 应用开发解决方案。同时Spring AI Alibaba能够很好的对接阿里云的其他服务,天然生态整合

快速上手

阿里云百炼的模型服务

大模型服务平台百炼控制台

        阿里云百炼是阿里云推出的企业级大模型服务平台,旨在为开发者和企业提供从模型调用、应用构建到生产部署的全链路解决方案,类似于前文提到的硅基流动,同样有免费额度。

pom

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

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

        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>
</dependencies>

yml

server:
  port: 8082
spring:
  application:
    name: spring-alibaba-demo
  ai:
    dashscope:
      api-key: sk-XXXXXX
logging:
  pattern:
    console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

测试demo

@RequestMapping("/ali")
@RestController
public class AliController {
    private final ChatClient dashScopeChatClient;
    
    public AliController(ChatClient.Builder chatClientBuilder) {
        this.dashScopeChatClient = chatClientBuilder.build();
    }

    @GetMapping("/chat")
    public String chat(String message) {
        return dashScopeChatClient.prompt(message).call().content();
    }
    
}

ChatClient

由于Spring AI Alibaba 是基于 Spring AI 开发的,因此 Spring AI ChatClient具备的功能,Spring AI Alibaba 大多也具备,比如流式响应,返回实体类等。

Chat Client-阿里云Spring AI Alibaba官网官网

测试demo

package com.example.alibaba.demo.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.List;

@RestController
@RequestMapping("/chat")
public class ChatController {
    @Autowired
    private ChatClient chatClient;

    @GetMapping("/call")
    public String call(String message) {
        return this.chatClient.prompt()
                .user(message)
                .call()
                .content();
    }

    //流式输出
    @GetMapping(value = "/stream", produces = "text/html;charset=utf-8")
    public Flux<String> stream(String message) {
        return this.chatClient.prompt()
                .user(message)
                .stream()
                .content();
    }

    //生成实体类
    record ActorFilms(String actor, List<String> movies) {
    }
    @GetMapping("/entity")
    public String entity(String actor) {
        ActorFilms actorFilms = chatClient.prompt()
                .user(String.format("帮我生成演员%s的作品", actor))
                .call()
                .entity(ActorFilms.class);
        return actorFilms.toString();
    }

}

效果

角色预设

@Configuration
public class ChatClientConfiguration {
    @Bean
    ChatClient chatClient(ChatClient.Builder builder) {
        return builder.defaultSystem("你叫小牛,在每次回复之前, 前面加一个字: {word}")
                .build();
    }

}

在每次调用前可以修改请求参数

​​​​@GetMapping("/word")
    public String word(String message, String word) {
        return chatClient.prompt()
                .system(sp -> sp.param("word", word))
                .user(message)
                .call()
                .content();
    }

更多的API前往官网

多模态

        多模态性指模型同时理解和处理文本、图像、音频及其他数据格式等多源信息的能力。

        人类通过多模态数据输入并行处理知识,我们的学习方式和体验都是多模态的 一不只有视觉、听觉或文本的单一感知。

        机器学习往往专注于处理单一模态的专用模型,例如,我们开发音频模型用于文本转语音或语音转文本任务,开发计算机视觉模型用于目标检测和分类等任务。

        然而,新一代多模态大语言模型正在兴起,例如 OpenAl的GPT-4o、Google的VertexAl Gemini 1.5、Anthropic 的 Claude3,以及开源模型 Llama3.2、LLaVA和 BakLLaVA,都能接受文本、图像、音频和视频等多种输入,并通过整合这些输入生成文本响应。

实践

Pom

 <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
</dependency>

        这个 Maven 依赖是阿里云灵积平台(DashScope)与 Spring AI 框架的集成启动器,核心作用是帮助 Spring 生态的 Java 项目快速对接阿里云灵积平台的 AI 能力,无需手动编写复杂的底层调用逻辑,降低 AI 应用开发门槛。

xml

server:
  port: 8082
spring:
  application:
    name: spring-alibaba-demo
  ai:
    dashscope:
      api-key: sk-d27ec6c50c1a448bbb7893d19af963a5
      chat:
        options:
          model: qwen-vl-max-latest
          multi-model: true #是否使用多模态
logging:
  pattern:
    console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

测试demo

@RestController
@RequestMapping("/multi")
public class MultiModelController {
    private final ChatClient chatClient;

    public MultiModelController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    @RequestMapping("/image")
    public String image(String prompt) throws URISyntaxException, MalformedURLException {
        String url = "https://dashscope.oss-cn-beijing.aliyuncs.com/images/dog_and_girl.jpeg";
        List<Media> mediaList=List.of(new Media(MimeTypeUtils.IMAGE_JPEG,new URI(url).toURL().toURI()));
        //定义用户模型

        UserMessage userMessage=UserMessage.builder().text(prompt).media(mediaList).build();
        return this.chatClient.prompt(new Prompt(userMessage)).call().content();
    }
}

 测试结果

智能聊天机器人

现如今,正值AI浪潮,我们可以看到很多的产品都会选择接入AI,去构建更符合产品场景的智能聊天机器人,去优化用户的体验

这里我们也可以去实现给自己的项目快速集成智能AI客服

环境搭建

pom

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

<dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
</dependency>

yml

server:
  port: 8082
spring:
  application:
    name: spring-chat-bot
  ai:
    dashscope:
      api-key: sk-d27ec6c50c1a448bbb7893d19af963a5


logging:
  pattern:
    console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

简单对话

@Configuration
public class ChatClientConfiguration {
    @Bean
    public ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                .defaultSystem("你叫中草药,是由双创实验室研发的一名智能AI助手,有着丰富的java编程和算法经验," +
                        "主要工作是帮助学生去解决在刷题过程中遇到的一些问题,协助解释分析算法原理时间空间复杂度等等")
                .build();
    }

}

采用流式输出,优化用户体验

@RestController
@RequestMapping("/chat")
public class ChatController {
    @Autowired
    private ChatClient chatClient;

    @RequestMapping(value = "/stream",produces = "text/html;charset=utf-8")
    public Flux<String> stream(String prompt){
        return chatClient.prompt()
                .user(prompt)
                .stream()
                .content();
    }

 
}

测试

对话记忆

        我们当前实现的会话功能本身是没有记忆功能的,“对话记忆”这一概念,指的是模型与用户进行交互式对话过程中。能够追踪、理解、并利用先前对话上下文的对话内容,并根据这些信息进行就绪的响应。

        大模型本身是不具备记忆能力的,要想让大模型记住之前的聊天内容,需要把之前的聊天内容与新的提示词一起发给大模型.

我们可以用 大模型服务平台百炼控制台 来验证一下

右侧返回了模型的响应,并且把模型的响应放到了assistant模块

        Spring AI 中的角色消息类型是支撑大语言模型对话系统实现上下文感知交互的核心机制,主要通过SystemMessageUserMessageAssistantMessage三种核心类型,结合时序链与注意力机制,构建起连贯且符合场景的对话流程:

三种核心消息类型的定位与作用

  • SystemMessage(系统消息):是对话的 “规则与背景锚点”,由系统在对话初始化 / 过程中设定,从系统层面定义对话核心要素:

    • 「背景」:明确对话场景(如翻译、客服、学术答疑等);
    • 「角色」:规定 AI 助手的身份(如 “你是专业翻译助手”“你是科普机器人”);
    • 「行为准则」:约束回复风格(如 “用简洁中文回答”“避免专业术语”)。
      它为 AI 的响应划定 “框架边界”,确保输出符合业务 / 场景预期。
  • UserMessage(用户消息):是对话的 “触发与推进动力”,由用户主动输入,包含问题、需求或闲聊内容,是推动对话进程的核心输入,决定 AI 需要响应的具体内容。

  • AssistantMessage(助手消息):是对话的 “AI 输出载体”,由 AI 模型基于 “系统设定 + 用户输入 + 历史对话” 生成,是 AI 与用户交互的直接反馈,体现模型对上下文的理解与回应能力。

再次与大模型对话,会发现模型具备之前的记忆

我们手动删掉前文内容,可以发现大模型不具备记忆能力

ChatMemory

        大型语言模型 (LLM) 是无状态的,也就是它们不会保留有关以前交互的信息。当开发人员希望在多个交互中维护上下文或状态时,这可能是一个限制。为了解决这个问题,Spring AI 提供了对话内存功能,定义了 ChatMemory 接口,允许开发人员在与 LLM 的多次交互中存储和检索信息。

@Configuration
public class ChatClientConfiguration {
    @Bean
    public ChatMemoryRepository chatMemoryRepository() {
        return new InMemoryChatMemoryRepository();
    }

    @Bean
    public ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .build();
    }

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder,ChatMemory chatMemory) {
        return builder
                .defaultSystem("你叫中草药,是由双创实验室研发的一名智能AI助手,有着丰富的java编程和算法经验," +
                        "主要工作是帮助学生去解决在刷题过程中遇到的一些问题,协助解释分析算法原理时间空间复杂度等等")
                .defaultAdvisors(new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory).build())
                .build();
    }

}

        使用InMemoryChatMemoryRepository(内存级存储),意味着对话历史仅保存在应用内存中,应用重启后数据会丢失,适合开发环境、简单场景(生产环境若需持久化,可替换为基于数据库的实现,如自定义 JdbcChatMemoryRepository)

        采用MessageWindowChatMemory:“窗口式对话记忆”,默认会保留最近的 N 条对话(可通过windowSize配置,如windowSize(10)表示只保留最近 10 轮),避免历史数据过多导致大模型请求 token 超标。

修改接口
@RestController
@RequestMapping("/chat")
public class ChatController {
    @Autowired
    private ChatClient chatClient;

    @Autowired
    private ChatMemory chatMemory;


    @RequestMapping(value = "/stream",produces = "text/html;charset=utf-8")
    public Flux<String> stream(String prompt,String chatId){
        return chatClient.prompt()
                .user(prompt)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
                .stream()
                .content();
    }

}

此时实现了对话记忆 

对话历史

提供对话历史栏,方便用户进行历史追溯

@Data
public class ChatInfo {
    private String chatId;
    private String title;


    public ChatInfo(String chatId, String title) {
        this.chatId = chatId;
        this.title = title==null?"无标题":title.length()>=15?title.substring(0,15)+"...":title;
    }
}

补充相应的接口

@RestController
@RequestMapping("/chat")
public class ChatController {
    @Autowired
    private ChatClient chatClient;

    @Autowired
    private ChatMemory chatMemory;

    @Autowired
    private ChatHistoryRepository chatHistoryRepository;


    @RequestMapping(value = "/stream",produces = "text/html;charset=utf-8")
    public Flux<String> stream(String prompt,String chatId){
        return chatClient.prompt()
                .user(prompt)
                .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
                .stream()
                .content();
    }

    /**
     * 获取会话列表
     * @return
     */
    @RequestMapping("/getChatIds")
    public List<ChatInfo> getChatIds(){
        return chatHistoryRepository.getChats();
    }

    /**
     * 获取会话记录
     * @param chatId
     */
    @RequestMapping("/getChatHistory")
    public List<MessageVO> getChatHistory(String chatId){
        List<Message> messages = chatMemory.get(chatId);
        return messages.stream().map(MessageVO::new).collect(Collectors.toList());
    }

    /**
     * 删除会话
     * @param chatId
     * @return
     */
    @RequestMapping("/deleteChat")
    public Boolean deleteChat(String chatId){
        try {
            chatHistoryRepository.clearByChatId(chatId);
            chatMemory.clear(chatId);
        }catch (Exception e){
            return false;
        }
        return true;
    }
}
@Data
public class MessageVO {
    String role;
    String content;

    public MessageVO(Message message) {
        switch (message.getMessageType()){
            case USER -> {this.role = "user"; break;}
            case ASSISTANT -> {this.role = "assistant"; break;}
            case SYSTEM -> {this.role = "system"; break;}
            case TOOL -> {this.role = "tool"; break;}
        }
        this.content = message.getText();
    }
}
@Repository
public class ChatMemoryHistoryRepository implements ChatHistoryRepository{

    private Map<String, String> chatInfos = new LinkedHashMap<>();

    /**
     * 保存会话记录
     * 如果是新会话, 则新增
     * 如果会话已经存在, 则更新
     */
    @Override
    public void save(String chatId, String title) {
        chatInfos.put(chatId, title);
    }

    @Override
    public void clearByChatId(String chatId) {
        chatInfos.remove(chatId);
    }

    @Override
    public List<ChatInfo> getChats() {
        return chatInfos.entrySet().stream()
                .map(enrty-> new ChatInfo(enrty.getKey(), enrty.getValue()))
                .collect(Collectors.toList());
    }
}

这样我们就搭建好了基于内存实现的智能聊天机器人,这样一个内嵌问答机器人,可以满足大多数需求


永久热烈,永久尽享欢愉。永久心跳,永久年少青春。——济慈

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

  制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐