当前位置: 首页 > news >正文

深入解析 Spring AI 系列:解析请求参数处理

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

image

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

Prompt

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

image

我们来看一下他的核心方法,这些方法是经过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 技术这块儿的奥秘。特爱跟人交流技术,喜欢把自己的心得和大家分享。还当上了腾讯云创作之星,阿里云专家博主,华为云云享专家,掘金优秀作者。各种征文、开源比赛的牌子也拿了。

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

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

http://www.dtcms.com/a/393695.html

相关文章:

  • OpenLayers地图交互 -- 章节五:捕捉交互详解
  • 阿瓦隆1566HA-448T矿机深度解析:性能、效率与冷却技术
  • 平替confluence,推荐一款国产开源免费的知识管理工具 - sward
  • 【开源】基于STM32的智能垃圾桶
  • RuoYi-Cloud问题:访问https的网关地址,实际是访问http的文件服务
  • HttpClientFactory vs new HttpClient:.NET Core HTTP 客户端的正确打开方式
  • MySQL数据库(七)—— 基于主主复制与 Keepalived 非抢占模式的高可用方案
  • 如何提高Java并发编程的实战能力?
  • JavaWeb 课堂笔记 —— 17 SpringBootWeb案例 部门管理
  • java设计模式四,原型模式
  • 【NOIP 2024 T2】遗失的赋值
  • TypeScript学习笔记1
  • Android普通应用切到后台后,多长时间会被系统回收
  • 【Elasticsearch面试精讲 Day 21】地理位置搜索与空间查询
  • 【Android】View 的滑动
  • 【深度学习的优化理论】如何理解OT与欧几里得距离均值的区别
  • 【Android】Room数据库的基本使用
  • 项目:仿muduo库的高并发服务器
  • Oracle普通用户报错ORA-31603处理
  • 网络安全期末大论文
  • 23种设计模式之【工厂方法模式】-核心原理与 Java实践
  • cocos 添加背景,帧动画,贴图
  • 亚马逊云科技重磅推出 Amazon S3 Vectors:首款大规模支持原生向量的云存储服务
  • SQLite Expert:一款功能强大的SQLite管理工具
  • Python 2025:供应链安全威胁与防御实战
  • 队列+宽搜(BFS)-429.N叉树的层序遍历-力扣(LeetCode)
  • 【Linux命令从入门到精通系列指南】rm 命令详解:安全删除文件与目录的终极实战手册
  • Springboot使用dockerfile-maven-plugin部署镜像
  • 安卓蓝牙键盘和鼠标6.10.4去更新汉化版 手机变为蓝牙键盘和鼠标
  • 工作笔记-----lwip的内存管理策略解析