深入解析Spring AI框架:在Java应用中实现智能化交互的关键 ...

打印 上一主题 下一主题

主题 827|帖子 827|积分 2481

今天我们的Spring AI源码分析主题即将结束。我已经对自己感兴趣的根本内容进行了全面的审视,并将这些分析分享给大家。假如你对这个主题感兴趣,可以阅读以下几篇文章。每篇文章都层层递进,深入探讨相关内容。考虑到长文可能让大家感到疲惫,我采用了渐渐推进的方式,确保每一篇都简明易懂,便于理解。希望能为你们提供有价值的参考!
Spring AI的根本用法:https://www.cnblogs.com/guoxiaoyu/p/18284842
Spring.3版本主动装配机制的演变与实践:https://www.cnblogs.com/guoxiaoyu/p/18384642
SpringBoot.3中的aot.factories到底有什么用:https://www.cnblogs.com/guoxiaoyu/p/18434660
Spring AI的壅闭式请求与响应机制的焦点逻辑:https://www.cnblogs.com/guoxiaoyu/p/18440488
Spring AI的流式答复源码分析:https://www.cnblogs.com/guoxiaoyu/p/18440684
今天我们的主题将聚焦于末了一步:怎样将AI技能有效应用于Java程序中。众所周知,Java是一种面向对象的编程语言,因此不论我们调用什么AI接口,从业务的角度来看,它本质上只是一个接口,而AI则充当了一个第三方对接平台。然而,值得留意的是,AI的聊天回复每每不实用于对象,因为这些回复无法直接返回格式化的JSON数据。这一问题导致Spring无法将其转化为实体类,从而无法真正融入业务流程。
今天,我们将探讨Spring AI框架是怎样有效解决这一挑战的。通过深入分析框架的设计和实现,我们希望为大家展示怎样将AI能力顺利整合到Java应用中,推动业务的进一步发展。
除此之外,function call 函数回调也是AI技能的一个重要特性。那么,Spring AI是怎样应对这一挑战的呢?今天,我们将深入探讨这个问题,解析Spring AI框架怎样有效处理函数回调,从而增强AI与Java程序之间的交互能力。
实体化类

实体类在Java程序中饰演着不可或缺的脚色,无论是进行内部操作,还是将数据返回给前端的RESTful接口,实体类都是业务中信息传递的焦点。在Spring AI框架中,我们可以有效地控制AI的答复,以确保其能够正确映射到实体类。接下来,我们将探讨Spring AI是怎样实现这一功能的,根本用法如下:
  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. }
复制代码
源码分析

在这里,我们不再直接调用 content 方法,而是选择使用 entity 方法作为返回范例。这一变化意味着我们必要重点关注 entity 的实现及其在整个流程中的作用。接下来,让我们通过代码示例来深入分析这一关键部分:
  1. public <T> T entity(Class<T> type) {
  2.       Assert.notNull(type, "the class must be non-null");
  3.       var boc = new BeanOutputConverter<T>(type);
  4.       return doSingleWithBeanOutputConverter(boc);
  5. }
复制代码
这里使用了一个名为 BeanOutputConverter 的转换器。接下来,我们来具体查看一下 doSingleWithBeanOutputConverter 方法的具体实现。
  1.         private <T> T doSingleWithBeanOutputConverter(StructuredOutputConverter<T> boc) {
  2.             var chatResponse = doGetObservableChatResponse(this.request, boc.getFormat());
  3.             var stringResponse = chatResponse.getResult().getOutput().getContent();
  4.             return boc.convert(stringResponse);
  5.         }
复制代码
在这里,我们要讨论的 doGetObservableChatResponse 方法主要负责与第三方 API 的交互过程。由于我们在之前的讲解中已经对聊天调用API方法的实现进行了具体分析,因此这次我们就不再深入探讨其具体内容,而是集中于方法的焦点功能和应用场景。
实体类提示词限制

在这里,我们来查看一下 boc.getFormat() 方法。这个方法返回一段提示词,而这些提示词会根据差别的范例而有所区别。为了更好地理解,我们可以具体分析一下单个 Bean 实体类所对应的提示词格式。

具体如下:
  1.     public String getFormat() {
  2.         String template = """
  3.                 Your response should be in JSON format.
  4.                 Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
  5.                 Do not include markdown code blocks in your response.
  6.                 Remove the ```json markdown from the output.
  7.                 Here is the JSON Schema instance your output must adhere to:
  8.                 ```%s```
  9.                 """;
  10.         return String.format(template, this.jsonSchema);
  11.     }
复制代码
这实在非常简单。通过使用提示词来明确限制 AI 返回的格式,可以最大水平地确保其输出符合我们的要求。这种方式使得 Spring 能够有效地进行解析,而 jsonSchema 则仅仅是我们传递的实体类的各种信息。
封装实体类

boc.convert 方法负责将数据封装成实体类的过程。具体来说,它会接收原始数据,并通过内部逻辑进行转换,以生成符合我们定义的实体类结构。

从表面上看,我们可以清晰地看出该过程涉及到 JSON 序列化,它将数据封装成我们所期望的对象格式。然而,必要留意的是,虽然 AI 的提示词旨在尽量限制其回复内容,以使其尽可能符合我们的要求,但由于各种因素的影响,我们无法保证其返回的格式会完全按照预设进行。
因此,为了确保程序的稳健性和可靠性,在此过程中引入了非常捕捉机制。这一机制能够有效地处理潜在的格式不一致或错误,从而确保应用在面对不符合预期的数据时,能够平稳运行而不至于崩溃。
函数回调

AI目前能够发挥一定作用,主要得益于模型的函数调勤劳能。假如仅仅依靠训练模型进行聊天答复,实在际价值是相对有限的,因为这种方式的成本非常高,许多企业难以承受。然而,随着函数回调功能的引入,AI可以实时访问和利用各种数据,包括实时数据和业务数据,使其能够根据提供的信息进行更为精准和有效的答复,从而具备了实质性的业务能力。
接下来,我们来看看Spring AI是怎样实现这一点的。
根本用法

了解了之前的 Spring AI 用法文章后,你大概已经掌握了怎样创建一个 Function 函数。接下来,我们将直接深入探讨怎样将这个函数添加到我们的项目中。
  1. @PostMapping("/ai-function")
  2. ChatDataPO functionGenerationByText(@RequestParam("userInput")  String userInput) {
  3.     String content = this.myChatClientWithSystem.prompt()
  4.             .user(userInput)
  5.             .functions("CurrentWeather")
  6.             .call()
  7.             .content();
  8.     log.info("content: {}", content);
  9.     ChatDataPO chatDataPO = ChatDataPO.builder().code("text").data(ChildData.builder().text(content).build()).build();;
  10.     return chatDataPO;
  11. }
复制代码
在我们的项目中,functions 函数允许添加多种功能,不仅仅局限于单一工具的调用。例如,在可视化智能体的应用中,如千帆 AppBuilder,我们可以观察到思索轮数的运用,这其中涉及了多个工具的调用。这种方式为我们的智能体提供了更丰富的功能和机动性。
接下来,我们将进行一次函数的调用,以实际展示其结果。

在这里,我们使用了一个固定的 30 度作为示例值,但你可以在函数方法内部通过接口调用其他第三方服务来获取实时数据。因此,通过集成外部数据源,你可以实现更为智能和适应性强的功能。
源码分析

还记得我们之前讨论过的内容吗?在答复的末了,我们会进行一次判断,以确定当前的输出是否为函数调用。这一过程是确保系统能够正确识别和实行函数的重要环节。接下来,我们将展示相关的源码,以便更深入地理解这一机制的具体实现:
  1. if (isToolCall(chatResponse,
  2.         Set.of(ChatCompletionFinishReason.TOOL_CALLS.name(), ChatCompletionFinishReason.STOP.name()))) {
  3.     var toolCallConversation = handleToolCalls(prompt, chatResponse);
  4.     // Recursively call the call method with the tool call message
  5.     // conversation that contains the call responses.
  6.     return this.call(new Prompt(toolCallConversation, prompt.getOptions()));
  7. }
复制代码
我也在这里设置了一个断点,以便大家可以清楚地看到这一过程。这一断点帮助我们确认,返回的结果完全是由 AI 生成的。在这个底子上,我们会进行进一步的判断,以决定是否必要调用函数工具。

接下来,我们将进入函数调用的过程。这一步调至关重要,因为函数的返回值将被再次提供给 AI,作为后续答复的参考。我们来看看具体是怎样进行函数调用的。虽然我已经找到了相关的源码,但为了让大家更容易理解这个过程,我将提供一张可视化的图片。这张图片将清晰地展示函数调用的流程,以及返回值是怎样被整合进 AI 的答复中的。

发送这些参数的原因在于,在发起请求时已经设置了相关限制。以下是我截取下来的请求参数:
tools=[FunctionTool[type=FUNCTION, function=Function[description=获取指定所在的天气情况, name=CurrentWeather, parameters={$schema=https://json-schema.org/draft/2020-12/schema, type=object, properties={location={type=string}, unit={type=string, enum=[C, F]}}}]]]
目前险些所有第三方AI接口都提供了一个名为 tools 的参数,用于传递我们必要的参数。以OpenAI为例:

调用函数接口

由于我们的函数实现了 @FunctionalInterface 接口,因此 call 这一行实际上会调用我们定义的 apply 接口。鉴于我们的参数是一个实体记录,系统会对其进行 JSON 转化和封装,随后再进行调用。具体过程如下所示:
  1.     public String call(String functionArguments) {
  2.         // Convert the tool calls JSON arguments into a Java function request object.
  3.         I request = fromJson(functionArguments, inputType);
  4.         // extend conversation with function response.
  5.         return this.andThen(this.responseConverter).apply(request);
  6.     }
复制代码
因此,即使所有操作都已结束,假如在下次 AI 判断中仍然必要调用工具,系统将继承进行循环,直到所有问题都得到完整的答复为止。这种设计确保了整个过程的连贯性和完整性。
总结

在这次探讨中,我们深入挖掘了Spring AI框架怎样与Java程序完美联合,提升业务能力。随着AI技能的不断发展,其在Java应用中的整合成为了提升开发效率和用户体验的关键。我们不仅分析了实体类的映射与控制,还探讨了函数回调的强盛功能,展示了怎样通过Spring AI有效处理这些复杂交互。
希望这些分析能够引发你对AI应用的灵感,并促使你在自己的项目中大胆尝试,将AI技能融入到业务流程中。期待未来能看到大家的创意实现和应用!
完结撒花!关于Spring AI的一系列源码分析到此结束,以后假如另有感兴趣的出发点,我也会继承为大家带来分析!

我是努力的小雨,一名 Java 服务端码农,潜心研究着 AI 技能的奥秘。我热爱技能交流与分享,对开源社区布满热情。同时也是一位腾讯云创作之星、阿里云专家博主、华为云云享专家、掘金优秀作者。


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦见你的名字

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表