Java智能之Spring AI:5分钟打造智能聊天模型的利器

news2024/9/21 16:16:43

前言

尽管Python最近成为了编程语言的首选,但是Java在人工智能领域的地位同样不可撼动,得益于强大的Spring框架。随着人工智能技术的快速发展,我们正处于一个创新不断涌现的时代。从智能语音助手到复杂的自然语言处理系统,人工智能已经成为了现代生活和工作中不可或缺的一部分。在这样的背景下,Spring AI 项目迎来了发展的机遇。尽管该项目汲取了Python项目如LangChain和LlamaIndex的灵感,但Spring AI并不是简单的移植。该项目的初衷在于推进生成式人工智能应用程序的发展,使其不再局限于Python开发者。

Spring AI 的核心理念是提供高度抽象化的组件,作为开发AI应用程序的基础。这些抽象化组件具备多种实现,使得开发者能够以最少的代码改动便捷地交换和优化功能模块。

具体而言,Spring AI 提供了支持多种主流模型提供商的功能,包括OpenAI、Microsoft、Amazon、Google和Hugging Face。支持的模型类型涵盖了从聊天机器人到文本生成、图像处理、语音识别等多个领域。而其跨模型提供商的可移植API设计,不仅支持同步和流式接口,还提供了针对特定模型功能的灵活选项。

此外,Spring AI 还支持将AI模型输出映射为POJO,以及与主流矢量数据库提供商(如Apache Cassandra、Azure Vector Search、MongoDB Atlas等)无缝集成的能力。其功能不仅局限于模型本身,还包括了数据工程中的ETL框架和各种便利的函数调用,使得开发AI应用程序变得更加高效和可靠。

快速实战

本期实战是我们的第一篇,旨在通过快速展示Spring AI项目,让大家了解它的优点和特性。为了方便大家使用,我还将本期的源代码提交到了仓库中,并加入了swagger-ui的API调用界面,使得使用起来更加便捷。如果你对此感兴趣,欢迎前往查看star。同时,我也会持续维护这个项目,确保它始终保持活跃。

仓库地址:GitHub - StudiousXiaoYu/spring-ai-demo: 专门演示官方spring-ai的各种用法,仅供学习参考~

项目生成

当我们开始时,首先需要创建一个项目结构。我们可以前往官方网站,快速生成Spring AI的依赖并创建项目。

image

聊天模型

在大型模型中,聊天模型扮演着至关重要的角色。那么,SpringAI是如何对其进行封装的呢?本期主要着重展示如何有效利用Spring AI的ChatClient,特别是在本示例中应用Spring AI的智能聊天模型。

日志级别

在这个过程中,如果想要查看请求的细节日志,务必将日志级别调整至DEBUG,具体操作如下:

image

模型配置

当我们使用一个模型时,必须首先在项目中加入相关的依赖,加入依赖后还需要在配置文件中填写相应的配置信息。

image

注入model

那么模型可以自动注入,我们可以直接使用它。在本期演示中,我们将展示三种自定义模型的注入方式,具体如下:

    private final ChatClient myChatClientWithSystem;

    private final ChatClient myChatClientWithParam;

    /**
     * 可以选择自动注入、也可以在方法内自定义,此客户端无系统文本
     */
    private final ChatClient chatClient;

    public MyController(ChatClient.Builder chatClientBuilder, MyChatClientWithSystem myChatClient, MyChatClientWithParam myChatClientWithParam) {
        this.chatClient = chatClientBuilder.build();
        this.myChatClientWithSystem = myChatClient.client();
        this.myChatClientWithParam = myChatClientWithParam.client();
    }

好的,让我来解释一下这三种情况:

  1. chatClient:这是默认的自动注入的ChatClient,不需要任何条件。

  2. myChatClientWithParam:这是一个注入系统文本并带有参数的ChatClient。

  3. myChatClientWithSystem:这是一个注入带有系统文本的ChatClient。

好的,第一种情况不需要处理,我们只需要通过配置类简单配置下面两种ChatClient。

@Configuration
class Config {

    @Bean
    MyChatClientWithSystem myChatClientWithSystem(ChatClient.Builder builder) {
        MyChatClientWithSystem build = MyChatClientWithSystem.builder()
                .client(builder.defaultSystem("你是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。身兼掘金优秀作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家等多重身份。")
                .build()).build();
        return build;
    }

    @Bean
    MyChatClientWithParam myChatClientWithParam(ChatClient.Builder builder) {
        MyChatClientWithParam build = MyChatClientWithParam.builder()
                .client(builder.defaultSystem("你是{user}。")
                        .build()).build();
        return build;
    }
}

简单文本回答

首先,让我们先来讨论一些简单的问答。

    @GetMapping("/ai")
    String generationByText(String userInput) {
        return this.chatClient.prompt()
            .user(userInput)
            .call()
            .content();
    }

在这段简练代码中,已经实现了各种封装和交互,为了更好地演示,我们来展示一下:

image

封装回答实体对象

大家都知道Java是一种面向对象的编程语言,因此在加入人工智能技术时,为了满足业务需求,将对象纳入其中是不可或缺的。那么,如何让人工智能的回答能够被Spring框架自动封装到对象中呢?让我们来探讨一下:

定义一个对象记录类:一个记录类(Record Class)的定义,名为 ActorFilms。用于封装相关字段记录类自动实现了 toString()、equals()、hashCode() 和 getter 方法,使得对象的字符串表示、相等性比较和哈希计算变得简单。你可以直接使用 actorFilms.toString()、actorFilms.equals(anotherActorFilms) 和 actorFilms.hashCode()。

public record ActorFilms(String actor, List<String> movies) {
}

    @GetMapping("/ai-Entity")
    ActorFilms generationByEntity() {
        ActorFilms actorFilms = chatClient.prompt()
                .user("Generate the filmography for a random actor.")
                .call()
                .entity(ActorFilms.class);
        return actorFilms;
    }

可以看到,只需简单地将entity设置为ActorFilms。接下来,我们需要检查返回的对象是否符合预期。

image

当用户输入信息后,系统返回一个实体类型的回答。这种实体类型的回答之所以能够被封装,是因为在发送信息时,系统不仅仅发送了用户输入的文本,还在其后添加了额外的信息。Generate the filmography for a random actor.\r\nYour response should be in JSON format.\r\nDo not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.\r\nDo not include markdown code blocks in your response.\r\nRemove the ```json markdown from the output.\r\nHere is the JSON Schema instance your output must adhere to:\r\n```{\r\n \"$schema\" : \"https://json-schema.org/draft/2020-12/schema\",\r\n \"type\" : \"object\",\r\n \"properties\" : {\r\n \"actor\" : {\r\n \"type\" : \"string\"\r\n },\r\n \"movies\" : {\r\n \"type\" : \"array\",\r\n \"items\" : {\r\n \"type\" : \"string\"\r\n }\r\n }\r\n }\r\n}```\r\n因此,当后续返回的数据为大型模型时,例如{"actor": "Emily Blunt", "movies": ["Edge of Tomorrow", "A Quiet Place", "The Devil Wears Prada", "Sicario", "Mary Poppins Returns"]},这样一来Spring就可以帮我将其自动封装起来了。

封装回答列表实体对象

当我们需要返回一个列表而不是一个对象时,可以轻松地利用Spring AI的封装功能来实现。让我们来看看如何操作:

    @GetMapping("/ai-EntityList")
    List<ActorFilms> generationByEntityList() {
        List<ActorFilms> actorFilms = chatClient.prompt()
                .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
                .call()
                .entity(new ParameterizedTypeReference<List<ActorFilms>>() {
                });
        return actorFilms;
    }

接使用ParameterizedTypeReference对象即可。为了让Spring能够自动封装返回结果,发送信息时也包含了返回格式信息作为提示。现在我们来查看演示的结果。

image

流式回答

在前面展示的示例中,大型模型一次性完成回答并将其全部输出给用户。然而,前端无法实现打字机效果,因此我们决定采用流式回答的方式来进行演示。

   @GetMapping("/ai-streamWithParam")
    Flux<String> generationByStreamWithParam() {
        var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorFilms>>() {
        });

        Flux<String> flux = this.chatClient.prompt()
                .user(u -> u.text("""
                            Generate the filmography for a random actor.
                            {format}
                          """)
                        .param("format", converter.getFormat()))
                .stream()
                .content();

        String content = flux.collectList().block().stream().collect(Collectors.joining());

        List<ActorFilms> actorFilms = converter.convert(content);
        log.info("actorFilms: {}", actorFilms);
        return flux;
    }

为了演示用户信息中的参数传递,我对流式回答进行了一个阻塞操作。如果不需要的话,可以将其删除。另外,由于我需要封装一个列表对象,所以进行了阻塞操作。实际上,这与上面提到的一样,即在问答中直接定义了大模型返回的格式。好的,我们来看一下返回结果。

image

带有系统信息的client

这次我们将演示客户端的配置。在对话中,我们知道有三种身份标识:system、user、assistant。至今,我们尚未展示系统身份标识,但之前我们已经定义了系统形式的客户端。因此,这次我们将直接使用它:

    @GetMapping("/ai-withSystemClient")
    Map<String, String> generationByTextWithSystemClient(String message) {
        return Map.of("completion", myChatClientWithSystem.prompt().user(message).call().content());
    }

这段代码非常简单,只需使用ChatClient即可。用户输入后,会返回一个Map类型的回答,其中key为"completion",对应的value为回答内容。让我们一起来看一下结果吧。

image

可以看出,实际上他已经将我的system信息包含在内了。

带有参数信息的client

当您需要演示带有参数的情况时,您可以考虑以下方法:在用户输入后,返回一个Map类型的回答,其中包含键值对,键为"completion",值为相应的回答。在实际业务场景中,参数是不可避免的,因此这种演示方式可以更好地展示人工智能的适用性。让我们继续探讨这一点:

    @GetMapping("/ai-withParamClient")
    Map<String, String> generationByTextWithParamClient(String message, String user) {
        return Map.of("completion", myChatClientWithParam.prompt().system(sp ->sp.param("user",user)).user(message).call().content());
    }

这里也是很简单的一句话,所以我们看下效果:

image

如果您对回答感到困惑,我们可以查看后台传输日志,以了解传输的参数详情。

image

可以注意到,实际上我们已经成功将参数设置完成。

聊天历史

在最后一个主要的业务场景中,每个人都会有自己的聊天记录。我们不能一直进行无状态的对话,这样会显得很不智能。因此,必须要有聊天记录的功能。虽然Spring AI尚未完全确定如何封装这部分功能,但已经提供了一个简单的对象类供我们调用。让我们来看一下:

    @GetMapping("/ai-chatMemory")
    String generationByChatMemory(HttpServletRequest request, String userInput) {
        String sessionId = request.getSession().getId();
        chatMemory.add(sessionId, new UserMessage(userInput));
        String content = this.chatClient.prompt()
                .advisors(new MessageChatMemoryAdvisor(chatMemory))
                .user(userInput)
                .call()
                .content();
        chatMemory.add(sessionId, new AssistantMessage(content));
        return content;
    }

实际上,在这种情况下,我们需要自行创建并维护一个聊天历史对象。因此,每次进行聊天前和聊天后,我们都应该将所需的信息添加到该对象中,然后直接使用它。让我们来看一下这种做法的效果:

image

image

可以看到,实际上在这里已经将历史记录一并呈现了出来。

总结

通过本文的介绍,我们深入了解了Spring AI项目的优势和特性,以及在实际应用中的快速实战示例。Spring AI作为一个高度抽象化的人工智能应用程序开发框架,为开发者提供了便捷的模型支持、灵活的功能模块交换和优化能力。它不仅能将AI模型输出映射为POJO,还能与主流矢量数据库提供商无缝集成,从而显著提升开发AI应用程序的效率和可靠性。

与Python相比,Java在企业级应用和大型系统中具有显著优势。Java语言的静态类型和严格的编译时检查使得代码更加健壮和易于维护,尤其适合需要高度可靠性和长期支持的项目。同时,Java生态系统的成熟度和广泛应用确保了开发者可以轻松找到丰富的库和工具支持,加速开发周期并降低项目风险。

希望本文能为您对Spring AI项目的理解和应用提供帮助,同时也欢迎您关注和使用这个项目,持续关注更新和维护。让我们一起见证人工智能技术的不断进步和应用!

文章转载自:努力的小雨

原文链接:https://www.cnblogs.com/guoxiaoyu/p/18284842

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1995231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【卫星遥感影像】国产遥感影像分类技术应用研究进展综述_论文推荐

影像分类是遥感影像信息提取中的基本问题之一和遥感影像应用的关键&#xff0c;为我国掌握本土信息资源自主权、满足国家的紧迫需求具有重大战略意义。本文将进行这篇遥感影像分类的论文推荐。 1. 论文引用 [1]胡杰,张莹,谢仕义.国产遥感影像分类技术应用研究进展综述[J].计算…

使用隔离式栅极驱动器的设计指南

本设计指南分为三部分&#xff0c;旨在讲解如何为电力电子应用中的功率开关器件选用合适的隔离栅极驱动器&#xff0c;并分享实战经验。本文为第一部分&#xff0c;主要包括隔离式栅极驱动器的介绍和选型指南。 本文引用地址&#xff1a; 安森美的隔离栅极驱动器专为满足 SiC…

阿里云CDN-边缘脚本EdgeScript的CI/CD实践

阿里云CDN-ES脚本CI/CD实践 背景环境项目代码结构及发布脚本代码1. 项目结构2. 发布工具代码 流水线配置1. 流程配置2. 脚本代码发布脚本说明0. 配置账户1. 清空测试环境&#xff08;回滚测试环境&#xff09;2. 执行脚本发布3. 发布&#xff08;测试环境推送到生产环境&#x…

java基础概念13-类和对象

一、面向对象OOP 类class&#xff1a;相同事物的共性的代码描述&#xff0c;所有类是引用数据类型。 对象&#xff08;实例&#xff09;instance&#xff1a;类的具体的一个个体的实物。 二、类的定义 在Java中&#xff0c;一个类&#xff08;Class&#xff09;是定义对象的…

【经验总结】ShardingSphere+Springboot-02 数据分片、标准分片算法、时间间隔分片算法

文章目录 三、分片算法配置3.1 数据分片3.1.2 垂直分片3.1.2 水平分片 3.2 &#xff08;标准&#xff09;分片算法3.2.1 INLINE 基于行表达式的分片算法 &#xff08;必须掌握&#xff09;关于是否开启范围查找 3.2.2 INTERVAL 时间范围分片算法 三、分片算法配置 3.1 数据分片…

vue2学习 -- 路由

文章目录 1. 相关理解2. 基本使用2.1 安装2.2 嵌套路由2.3 路由传参2.3.1 query2.3.2 路由的命名2.3.3 params 2.4 路由的props配置2.5 routerlink的replace属性 3. 编程式路由导航4. 缓存路由组件5. 两个新的生命周期钩子6. 路由守卫6.1 全局前置 / 后置路由守卫6.2 独享路由守…

Java面试题--JVM大厂篇之Java中Parallel GC的调优技巧与最佳实践

目录 引言&#xff1a; 正文&#xff1a; 1. 理解Parallel GC的工作原理 2. 常见痛点与解决方案 痛点一&#xff1a;长时间暂停 痛点二&#xff1a;频繁的Minor GC 痛点三&#xff1a;内存溢出 3. 调优参数推荐 4. 实战经验分享 结束语&#xff1a; 引言&#xff1a;…

海思AE模块Lines_per_500ms参数的意义

​ 基础知识 1秒(S)1000毫秒(ms)1000_000微妙(s)1000_000_000纳秒(ns) 1GHz1000Mhz1000_000KHz1000_000_000Hz 1Hz1/s 抗频闪原理 海思AE模块参数中有一个LinesPer500ms的参数&#xff0c;意思为500ms对应的曝光行数。此个参数和抗频闪有关。 我们知道&#xff1a; 50HZ…

SQL注入实例(sqli-labs/less-17)

0、初始网页 1、确定闭合字符 注入点在于password框&#xff0c;闭合字符为单引号 2、爆库名 1 and updatexml(1,concat(0x7e,database(),0x7e),1)# 1 and (select 1 from (select count(*),concat((select database()),floor(rand()*2))x from information_schema.tables gr…

【C语言】通讯录的实现(基本版和动态版)

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html &#x1f381;代码托管:C语言: C语言方向&#xff08;基础知识和应用&#xff09; (gitee.com) ⚙️操作环境:Visual Studio 2022 目录 一、…

教育法、义务教育法、教师法、未成年人保护法、预防未成年人犯罪法、学生伤害事故处理办法

《中华人民共和国教育法》 《中华人民共和国义务教育法》 《中华人民共和国教师法》 《中华人民共和国未成年人保护法》 《中华人民共和国预防未成年人犯罪法》 《学生伤害事故处理办法》

C++:string

1.STL简介 STL&#xff08;standard template library标准模版库&#xff09;&#xff0c;是c标准库的重要组成部分&#xff0c;是一个包罗数据结构与算法的软件框架。 STL有很多版本&#xff0c;我们学习STL要阅读部分源代码&#xff0c;主要参考SGI版本。 STL的六大组件&a…

Volana:一款基于Go开发的Shell命令代码混淆工具

关于Volana Volana是一款功能强大的Shell命令代码混淆工具&#xff0c;该工具基于Go语言开发&#xff0c;可以帮助广大研究人员实现对Shell命令或脚本代码的混淆处理。 在红队测试过程中&#xff0c;隐蔽性是非常重要的一个方面&#xff0c;许多基础设施会记录命令并实时将其发…

Java 中的泛型 集合(List,Set) Map

泛型的本质是参数化类型,即允许在编译时对集合进行类型检查,从而避免安全问题,提高代码的复用性 泛型的具体定义与作用 定义:泛型是一种在编译阶段进行类型检查的机制,它允许在类,方法,接口后通过<> 来声明类型参数.这些参数在编译时会被具体的类型替换.java在运行时,会通…

Java生成Word->PDF->图片:基于poi-tl 进行word模板渲染

文章目录 引言I Java生成Word、PDF、图片文档获取标签渲染数据生成文档案例II 工具类封装2.1 word 渲染和word 转 pfd2.2 pdf转成一张图片III poi-tl(word模板渲染) 标签简介文本标签{{var}}图片标签表格标签引用标签IV poi-tl提供了类 Configure 来配置常用的设置标签类型前后…

dwg图纸识别,提取建筑外轮廓坐标数据

1.业务流程说明 目的是通过dwg图纸&#xff0c;在网页端绘制出一个包括建筑外轮了的白模。为了达到这个目的&#xff0c;我们需要dwg图纸识别&#xff0c;提取到图纸中的建筑外轮廓的坐标数据。 2. 实施步骤 1.1 根据dwg图纸&#xff0c;转换成dxf文件&#xff0c;通过对dxf文…

区域与语言CultureInfo

CultureInfo 类 命名空间: System.Globalization 程序集: System.Globalization.dll 提供有关特定区域性&#xff08;对于非托管代码开发&#xff0c;则称为“区域设置”&#xff09;的信息。 这些信息包括区域性的名称、书写系统、使用的日历、字符串的排序顺序以及对日期…

56 锐键交换机开局

锐键交换机开局 一 锐键视图切换 1 Ruijie> 用户视图 2 Ruijie# 特权模式 3 Ruijie(config)# 全局配置模式 4 Ruijie(config-if-GigabitEthernet 1/1/1)# 接口配置模式 5 Ruijie(config)#show vlan 6 exit (退出) 7 enable(进入)

【电子数据取证】支持最新版微信、企业微信、钉钉等重点应用数据提取分析!

文章关键词&#xff1a;电子数据取证、手机取证、云取证、电子物证、仿真取证 针对取证调查员目前在案件现场无法提取通讯聊天数据的情况&#xff0c;为了更好地适应这一实战需求&#xff0c;龙信科技快速响应对A303“鹰眼”介质快取系统和A315计算机快速采集系统全面升级&…

java10-集合框架

1. 集合的简介 1.1什么是集合 集合Collection&#xff0c;也是一个数据容器&#xff0c;类似于数组&#xff0c;但是和数组是不一样的。集合是一个可变的容器&#xff0c;可以随时向集合中添加元素&#xff0c;也可以随时从集合中删除元素。另外&#xff0c;集合还提供了若干个…