ToB企服应用市场:ToB评测及商务社交产业平台

标题: Java智能之Spring AI:5分钟打造智能聊天模型的利器 [打印本页]

作者: 汕尾海湾    时间: 2024-7-9 09:46
标题: Java智能之Spring AI:5分钟打造智能聊天模型的利器
前言

尽管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。同时,我也会持续维护这个项目,确保它始终保持活泼。
堆栈地址:https://github.com/StudiousXiaoYu/spring-ai-demo
项目生成

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

聊天模型

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

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

模型设置

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

注入model

那么模型可以主动注入,我们可以直接使用它。在本期演示中,我们将展示三种自界说模型的注入方式,详细如下:
  1.     private final ChatClient myChatClientWithSystem;
  2.     private final ChatClient myChatClientWithParam;
  3.     /**
  4.      * 可以选择自动注入、也可以在方法内自定义,此客户端无系统文本
  5.      */
  6.     private final ChatClient chatClient;
  7.     public MyController(ChatClient.Builder chatClientBuilder, MyChatClientWithSystem myChatClient, MyChatClientWithParam myChatClientWithParam) {
  8.         this.chatClient = chatClientBuilder.build();
  9.         this.myChatClientWithSystem = myChatClient.client();
  10.         this.myChatClientWithParam = myChatClientWithParam.client();
  11.     }
复制代码
好的,让我来表明一下这三种情况:
好的,第一种情况不必要处理,我们只必要通过设置类简朴设置下面两种ChatClient。
  1. @Configuration
  2. class Config {
  3.     @Bean
  4.     MyChatClientWithSystem myChatClientWithSystem(ChatClient.Builder builder) {
  5.         MyChatClientWithSystem build = MyChatClientWithSystem.builder()
  6.                 .client(builder.defaultSystem("你是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。身兼掘金优秀作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家等多重身份。")
  7.                 .build()).build();
  8.         return build;
  9.     }
  10.     @Bean
  11.     MyChatClientWithParam myChatClientWithParam(ChatClient.Builder builder) {
  12.         MyChatClientWithParam build = MyChatClientWithParam.builder()
  13.                 .client(builder.defaultSystem("你是{user}。")
  14.                         .build()).build();
  15.         return build;
  16.     }
  17. }
复制代码
简朴文本回答

首先,让我们先来讨论一些简朴的问答。
  1.     @GetMapping("/ai")
  2.     String generationByText(String userInput) {
  3.         return this.chatClient.prompt()
  4.             .user(userInput)
  5.             .call()
  6.             .content();
  7.     }
复制代码
在这段简练代码中,已经实现了各种封装和交互,为了更好地演示,我们来展示一下:

封装回答实体对象

大家都知道Java是一种面向对象的编程语言,因此在到场人工智能技术时,为了满意业务需求,将对象纳入其中是不可或缺的。那么,如何让人工智能的回答能够被Spring框架主动封装到对象中呢?让我们来探讨一下:
界说一个对象纪录类:一个纪录类(Record Class)的界说,名为 ActorFilms。用于封装相关字段纪录类主动实现了 toString()、equals()、hashCode() 和 getter 方法,使得对象的字符串表示、相等性比较和哈希计算变得简朴。你可以直接使用 actorFilms.toString()、actorFilms.equals(anotherActorFilms) 和 actorFilms.hashCode()。
  1. public record ActorFilms(String actor, List<String> movies) {
  2. }
复制代码
  1.     @GetMapping("/ai-Entity")
  2.     ActorFilms generationByEntity() {
  3.         ActorFilms actorFilms = chatClient.prompt()
  4.                 .user("Generate the filmography for a random actor.")
  5.                 .call()
  6.                 .entity(ActorFilms.class);
  7.         return actorFilms;
  8.     }
复制代码
可以看到,只需简朴地将entity设置为ActorFilms。接下来,我们必要检查返回的对象是否符合预期。

当用户输入信息后,系统返回一个实体类型的回答。这种实体类型的回答之所以能够被封装,是由于在发送信息时,系统不仅仅发送了用户输入的文本,还在厥后添加了额外的信息。
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的封装功能来实现。让我们来看看如何操作:
  1.     @GetMapping("/ai-EntityList")
  2.     List<ActorFilms> generationByEntityList() {
  3.         List<ActorFilms> actorFilms = chatClient.prompt()
  4.                 .user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
  5.                 .call()
  6.                 .entity(new ParameterizedTypeReference<List<ActorFilms>>() {
  7.                 });
  8.         return actorFilms;
  9.     }
复制代码
直接使用ParameterizedTypeReference对象即可。为了让Spring能够主动封装返回结果,发送信息时也包含了返回格式信息作为提示。现在我们来查看演示的结果。

流式回答

在前面展示的示例中,大型模型一次性完成回答并将其全部输出给用户。然而,前端无法实现打字机效果,因此我们决定采用流式回答的方式来进行演示。
  1.    @GetMapping("/ai-streamWithParam")
  2.     Flux<String> generationByStreamWithParam() {
  3.         var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorFilms>>() {
  4.         });
  5.         Flux<String> flux = this.chatClient.prompt()
  6.                 .user(u -> u.text("""
  7.                             Generate the filmography for a random actor.
  8.                             {format}
  9.                           """)
  10.                         .param("format", converter.getFormat()))
  11.                 .stream()
  12.                 .content();
  13.         String content = flux.collectList().block().stream().collect(Collectors.joining());
  14.         List<ActorFilms> actorFilms = converter.convert(content);
  15.         log.info("actorFilms: {}", actorFilms);
  16.         return flux;
  17.     }
复制代码
为了演示用户信息中的参数传递,我对流式回答进行了一个壅闭操作。如果不必要的话,可以将其删除。另外,由于我必要封装一个列表对象,所以进行了壅闭操作。现实上,这与上面提到的一样,即在问答中直接界说了大模型返回的格式。好的,我们来看一下返回结果。

带有系统信息的client

这次我们将演示客户端的设置。在对话中,我们知道有三种身份标识:system、user、assistant。至今,我们尚未展示系统身份标识,但之前我们已经界说了系统形式的客户端。因此,这次我们将直接使用它:
  1.     @GetMapping("/ai-withSystemClient")
  2.     Map<String, String> generationByTextWithSystemClient(String message) {
  3.         return Map.of("completion", myChatClientWithSystem.prompt().user(message).call().content());
  4.     }
复制代码
这段代码非常简朴,只需使用ChatClient即可。用户输入后,会返回一个Map类型的回答,其中key为"completion",对应的value为回答内容。让我们一起来看一下结果吧。

可以看出,现实上他已经将我的system信息包含在内了。
带有参数信息的client

当您必要演示带有参数的情况时,您可以考虑以下方法:在用户输入后,返回一个Map类型的回答,其中包含键值对,键为"completion",值为相应的回答。在现实业务场景中,参数是不可避免的,因此这种演示方式可以更好地展示人工智能的适用性。让我们继承探讨这一点:
  1.     @GetMapping("/ai-withParamClient")
  2.     Map<String, String> generationByTextWithParamClient(String message, String user) {
  3.         return Map.of("completion", myChatClientWithParam.prompt().system(sp ->sp.param("user",user)).user(message).call().content());
  4.     }
复制代码
这里也是很简朴的一句话,所以我们看下效果:

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

可以留意到,现实上我们已经成功将参数设置完成。
聊天汗青

在最后一个主要的业务场景中,每个人都会有自己的聊天纪录。我们不能不停进行无状态的对话,如许会显得很不智能。因此,必须要有聊天纪录的功能。固然Spring AI尚未完全确定如何封装这部分功能,但已经提供了一个简朴的对象类供我们调用。让我们来看一下:
  1.     @GetMapping("/ai-chatMemory")
  2.     String generationByChatMemory(HttpServletRequest request, String userInput) {
  3.         String sessionId = request.getSession().getId();
  4.         chatMemory.add(sessionId, new UserMessage(userInput));
  5.         String content = this.chatClient.prompt()
  6.                 .advisors(new MessageChatMemoryAdvisor(chatMemory))
  7.                 .user(userInput)
  8.                 .call()
  9.                 .content();
  10.         chatMemory.add(sessionId, new AssistantMessage(content));
  11.         return content;
  12.     }
复制代码
现实上,在这种情况下,我们必要自行创建并维护一个聊天汗青对象。因此,每次进行聊天前和聊天后,我们都应该将所需的信息添加到该对象中,然后直接使用它。让我们来看一下这种做法的效果:


可以看到,现实上在这里已经将汗青纪录一并呈现了出来。
总结

通过本文的介绍,我们深入了解了Spring AI项目的优势和特性,以及在现实应用中的快速实战示例。Spring AI作为一个高度抽象化的人工智能应用程序开发框架,为开发者提供了便捷的模型支持、机动的功能模块互换和优化能力。它不仅能将AI模型输出映射为POJO,还能与主流矢量数据库提供商无缝集成,从而显着提升开发AI应用程序的服从和可靠性。
与Python相比,Java在企业级应用和大型系统中具有显着优势。Java语言的静态类型和严酷的编译时检查使得代码更加健壮和易于维护,尤其得当必要高度可靠性和长期支持的项目。同时,Java生态系统的成熟度和广泛应用确保了开发者可以轻松找到丰富的库和工具支持,加速开发周期并降低项目风险。
盼望本文能为您对Spring AI项目的理解和应用提供资助,同时也欢迎您关注和使用这个项目,持续关注更新和维护。让我们一起见证人工智能技术的不断进步和应用!
我是积极的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。身兼掘金良好作者、腾讯云内容共创官、阿里云专家博主、华为云云享专家等多重身份。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4