大家在使用Spring AI项目开发Agent时,可能会发现,尽管外层的接口设计和调用逻辑比较统一,但实际上每个第三方接口在实现时都会有一些微妙的差异。这些差异可能体现在请求参数的构造、数据格式的处理,或者是某些接口特有的配置选项上。因此,今天我们主要聚焦于Spring AI在实际调用接口之前,如何处理和构造请求参数的过程。以下是核心代码示意:

我们今天主要关注我用方框标记的关键部分,这些部分是在调用API接口之前进行的自适应兼容处理。因此,理解这些内容是非常重要的。如果你打算对接第三方模型,类似的自适应兼容处理也是必不可少的,你需要根据具体情况进行相应的调整和实现。

Prompt

这个Prompt在执行之前会经过多个复杂的预处理步骤。虽然每个单元测试本身相对简单,但在开发过程中,他前面也会有各种advisor做各种处理,这些处理在实际运行时并不直接显现,而是作为封装的一部分,最终在内部动态地被添加和实现。具体流程和细节可以通过下图所示的结构来进一步理解。

我们来看一下他的核心方法,这些方法是经过Spring AI 抽象和优化后提炼出来的。我们只需要对其有一个大致的了解即可,基本无法个人做处理的。

public Prompt toPrompt() {

        var messages = new ArrayList<Message>(this.messages());

        String processedSystemText = this.systemText();
//省略部分代码
messages.add(new SystemMessage(processedSystemText));
//省略部分代码
messages.add(new UserMessage(processedUserText, this.media()));
//省略部分代码
if (this.chatOptions() instanceof FunctionCallingOptions functionCallingOptions) {
if (!this.functionNames().isEmpty()) {
functionCallingOptions.setFunctions(new HashSet<>(this.functionNames()));
}
if (!this.functionCallbacks().isEmpty()) {
functionCallingOptions.setFunctionCallbacks(this.functionCallbacks());
}
if (!CollectionUtils.isEmpty(this.toolContext())) {
functionCallingOptions.setToolContext(this.toolContext());
}
}
return new Prompt(messages, this.chatOptions());
}

这部分省略了各种判断条件和需要替代的参数信息,简化后的表达仅关注核心的业务逻辑。实际上,整体流程的关键步骤是:首先将历史聊天记录添加进来,接着是添加系统提示词,最后再将本次用户提问的问题内容加入,从而完成整个信息的整合。

createRequest

这部分主要是由各个模型根据自身的需求进行适配和处理的。具体来说,它们会将所需的数据从Prompt中提取出来,经过适当的封装后,作为请求参数传递到相应的API接口中。然后,通过直接调用这些API接口,就能够获取到所需的结果。接下来,我们可以深入了解OpenAI是如何进行这一系列处理的。

ChatCompletionRequest createRequest(Prompt prompt, boolean stream) {

        List<ChatCompletionMessage> chatCompletionMessages = prompt.getInstructions().stream().map(message -> {
if (message.getMessageType() == MessageType.USER || message.getMessageType() == MessageType.SYSTEM) {
//省略部分代码
return List.of(new ChatCompletionMessage(content,
ChatCompletionMessage.Role.valueOf(message.getMessageType().name())));
}
else if (message.getMessageType() == MessageType.ASSISTANT) {
//省略部分代码
return List.of(new ChatCompletionMessage(assistantMessage.getText(),
ChatCompletionMessage.Role.ASSISTANT, null, null, toolCalls, null, audioOutput));
}
else if (message.getMessageType() == MessageType.TOOL) {
//省略部分代码
return toolMessage.getResponses()
.stream()
.map(tr -> new ChatCompletionMessage(tr.responseData(), ChatCompletionMessage.Role.TOOL, tr.name(),
tr.id(), null, null, null))
.toList();
}
else {
throw new IllegalArgumentException("Unsupported message type: " + message.getMessageType());
}
}).flatMap(List::stream).toList(); ChatCompletionRequest request = new ChatCompletionRequest(chatCompletionMessages, stream); Set<String> enabledToolsToUse = new HashSet<>(); if (prompt.getOptions() != null) {
//省略部分代码
request = ModelOptionsUtils.merge(updatedRuntimeOptions, request, ChatCompletionRequest.class);
} if (!CollectionUtils.isEmpty(this.defaultOptions.getFunctions())) {
enabledToolsToUse.addAll(this.defaultOptions.getFunctions());
} request = ModelOptionsUtils.merge(request, this.defaultOptions, ChatCompletionRequest.class);
//省略部分代码
request = ModelOptionsUtils.merge(
OpenAiChatOptions.builder().tools(this.getFunctionTools(enabledToolsToUse)).build(), request,
ChatCompletionRequest.class);
//省略部分代码
return request;
}

在去除所有干扰性的判断后,我们可以清晰地看到其核心逻辑:首先,它会从Prompt中提取出所有的聊天信息,然后将这些信息封装到一个自定义的请求体中。接着,它会解析这些数据,并根据需要添加一些可操作的选项(如模型名称、topp等参数)。最后,系统会将工具调用的相关信息一并加入请求。

需要特别注意的是,ChatCompletionRequest 这个类,前面说过它其实是接口对接文档中各种参数的体现。将这些参数封装完毕后,调用模型的API就变得非常顺畅和直接。因此,无论前面的过程如何抽象,Spring AI始终为你提供了一个兼容并可修改的暴露接口,其中一个关键的接口就是这个 call 方法。

总结

总的来说,Spring AI项目通过对请求参数的自适应兼容处理,简化了与第三方接口的交互过程。通过对参数构造和数据处理的精细化管理,确保了无论是在功能调用,还是在API对接中,都能稳定、高效地工作。在我们深入分析了 toPrompt()createRequest() 方法的实现后,可以看到,它们通过抽象和封装有效地处理了复杂的接口请求,确保了用户的输入和系统的响应能够高效流畅地匹配。

对于开发者而言,理解这些核心方法和自适应处理逻辑,不仅能提升开发效率,也能在对接第三方模型时更加得心应手。因此,掌握Spring AI在接口调用和参数处理中的精髓,对于成功开发和集成智能代理应用至关重要。


我是努力的小雨,一个正经的 Java 东北服务端开发,整天琢磨着 AI 技术这块儿的奥秘。特爱跟人交流技术,喜欢把自己的心得和大家分享。还当上了腾讯云创作之星,阿里云专家博主,华为云云享专家,掘金优秀作者。各种征文、开源比赛的牌子也拿了。

想把我在技术路上走过的弯路和经验全都分享出来,给你们的学习和成长带来点启发,帮一把。

欢迎关注努力的小雨,咱一块儿进步!

深入解析 Spring AI 系列:解析请求参数处理的更多相关文章

  1. Spring MVC 接受的请求参数

    目录 1. 概述 2. 详解 2.1 处理查询参数 2.2 处理路径参数接受输入 2.3 处理表单 3. 补充内容 3.1 Ajax/JSON 输入 3.2 multipart参数 3.3 接收 he ...

  2. Spring 中Controller 获取请求参数的方法笔记

    1.直接把表单的参数写在Controller相应的方法的形参中,适用于get方式提交,不适用于post方式提交.若"Content-Type"="application/ ...

  3. Spring MVC的Post请求参数中文乱码的原因&处理

    一.项目配置: Spring 4.4.1-RELEASE Jetty 9.3.5 JDK 1.8 Servlet 3.1.0 web.xml文件中没有配置编解码Filter 二.实际遇到的问题:客户端 ...

  4. Spring MVC如何接收浏览器传递来的请求参数--request--形参--实体类封装

    阅读目录 1. 通过HttpServletRequest获得请求参数和数据 2. 处理方法形参名==请求参数名 3. 如果形参名跟请求参数名不一样怎么办呢?用@RequestParam注解 4. 用实 ...

  5. 0056 Spring MVC如何接收浏览器传递来的请求参数--request--形参--实体类封装

    浏览器总会向服务器传递一些参数,那么Spring MVC如何接收这些参数? 先写个简单的html,向服务器传递一些书籍信息,如下: <!DOCTYPE html> <html> ...

  6. SpringMVC RequestMapping & 请求参数

    SpringMVC 概述 Spring 为展现层提供的基于 MVC 设计理念的优秀的Web 框架,是目前最主流的 MVC 框架之一 Spring3.0 后全面超越 Struts2,成为最优秀的 MVC ...

  7. Spring MVC的映射请求

    一.SpringMVC常用注解 @Controller 声明Action组件 @Service    声明Service组件    @Service("myMovieLister" ...

  8. 使用 POJO 对象绑定请求参数

    概述 Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值并且支持级联属性.这一特性在日常开发过程中使用频率比较高,开发效率也高,本文主要对 POJO 对象绑定 ...

  9. SpringMVC——映射请求参数

    Spring MVC 通过分析处理方法的签名,将 HTTP 请求信息绑定到处理方法的相应人参中. @PathVariable @RequestParam @RequestHeader 等) Sprin ...

  10. SpringMVC听课笔记(四:映射请求参数 & 请求头)

    1.请求参数 @RequestParam  来映射请求参数  http://localhost:8080/springmvc-1/springmvc/testRequestParam?username ...

随机推荐

  1. OpenGL编程指南(原书第9版)

    这本书是<OpenGL编程指南(原书第9版)>,也称为<OpenGL Programming Guide: The Official Guide to Learning OpenGL ...

  2. Postgresql之基础

    Postgresql:  https://www.postgresql.org/ [安装] 删除已经存在的pg: yum remove -y postgresql* && rm -rf ...

  3. monitor磁盘空间不足警告

    虚拟机安装ceph时,执行ceph -s monitor主机遇到了 mon c101(monitor主机名) is low on available space 错误 这是我找到的解决办法 monit ...

  4. vue表格轮播插件

    1.前言 需求:制作大屏看板时,经常要展示表格数据,通常一页时放不下的,表格需要自动滚动,并维持表头固定 为何自己封装:网上的滚动组件有2类,一种传入json数据进行滚动(DataV),优点是可以做到 ...

  5. window.open打开网址被拦截

    window.open打开网址被拦截 标签: js 坑位 通过window.open打开一个网址,在火狐和IE系列浏览器下会拦截掉,除非用户主动点击允许才会成功,这样用户体验基本是恶心到产品的,而产品 ...

  6. openEuler欧拉设置git pull免密

    使用git config命令在本地全局设置用户名和邮箱 git config --global user.name "username":全局添加用户名 git config -- ...

  7. FineReport取消强制分页和调整宽度的设置方法

    在decision里,找到管理系统-目录管理,打开相应挂载的报表,在参数设置里,添加以下内容: _bypagesize_ 字符串 false

  8. TaurusDB库表时间点极速恢复,大幅缩短数据恢复时间

    经过多组实验对比,对于大实例下仅需恢复几张表数据的情况,有显著优化效果.尤其针对游戏业务等需要频繁回档的场景,将大幅度缩短因数据恢复导致的停服时间.后续我们将逐步在公有云上开放此特性,以惠及更多用户. ...

  9. Java 和 native 的一些进展

    GraalVM Java 生成 DLL/SO https://medium.com/graalvm/3-ways-to-polyglot-with-graalvm-fb28c1542b45 Proje ...

  10. 区块链技术已经衰落了吗?(区块链已die)

    区块链技术已经好多年没有听到有人提了,不过比特币却一直是不是的又新闻出现,当然国内已经把比特币交易归入到了不合法的地位了.区块链技术是国家战略的技术,但是这个技术说实话确实不是很高深,或者说蛮easy ...