首页 > 其他分享 >深入解析 Spring AI 系列:解析请求参数处理

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

时间:2025-01-23 17:36:26浏览次数:1  
标签:省略 AI Spring 接口 new message 解析

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

标签:省略,AI,Spring,接口,new,message,解析
From: https://www.cnblogs.com/guoxiaoyu/p/18677050

相关文章

  • 在使用prism的region跳转时,出现The region manager does not contain the MainViewReg
    在做新项目时,把原来的旧项目拷过来进行重构,上一个项目进行region填充是没有问题的,这次再次进行测试出现了这样的问题,于是在网上寻找答案。错误给出来的很明显,regionManager没有一个叫做MainViewRegionName的区域,想当然的就手动添加,进行刷新,这种方法参考Prism区域异常问题分析(......
  • C++模板全解析:场景与注意点揭秘!
    C++作为现代编程语言中的一种,其强大功能和复杂性使得它在系统编程、应用开发等领域广受欢迎。其中,模板(Template)是C++语言中一个极为重要且强大的特性,它不仅提高了代码的复用性,还使得类型无关的编程成为可能。本文将详细介绍C++模板的基础知识,包括其概念、分类、常见应用场景及......
  • 在Rust项目中,一般测试用例函数 #[(test)] 都写在哪里? 如果要测试 main.rs 文件中的函
    eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee在Rust项目中,测试用例通常放在以下两个位置:1.与源代码同文件中的模块测试用例函数一般写在与实现代码同一个文件中,放在一个名为tests的模块里,使用#[cfg(test)]注解。这种方式适用于对模块内的函数或逻辑进行单元测试。示......
  • 让万物「听说」:AI 对话式智能硬件方案和发展洞察
    本文整理自声网SDK新业务探索组技术负责人,IoT行业专家@吴方方1月18日在RTE开发者社区「VoiceAgent+硬件分享会」上的分享。本次主要介绍了AI对话式智能硬件的发展历程,新一波AI浪潮所带来的创新机遇、技术挑战以及未来的展望。 在语音交互浪潮的推动下,AIoT行业......
  • (一)一文读懂transformers库中常见组件PreTrainedModel,PretrainedConfig,AutoTokenizer
    文章目录一、训练管理大师:`Trainer`和`TrainingArguments``TrainingArguments`:训练的“魔法配方”`Trainer`:训练的“超级厨师”二、数据整理小能手:`DataCollatorWithPadding`三、因果语言模型的输出管家:`CausalLMOutputWithPast`四、模型加载与处理的智能助手:`AutoPro......
  • 数据分析和AI丨拒绝AI技术焦虑,工程领域AI应用的八大技巧
     现今,人们正在对科技发展以及AI技术进行无限探索,在这个过程中,很多工程师可能会感受到“有心无力”,很大程度是因为他们尚未实现自己的第一个可落地执行的人工智能(AI)应用案例。这个结果让人感到十分惊讶,因为目前在工程领域人们对AI的兴趣或投资并不缺乏。研究表明,86%的工......
  • Android Systrace 基础知识 - MainThread 和 RenderThread 解读
    正文这里以滑动列表为例,我们截取主线程和渲染线程一帧的工作流程(每一帧都会遵循这个流程,不过有的帧需要处理的事情多,有的帧需要处理的事情少),重点看“UIThread”和RenderThread这两行这张图对应的工作流程如下主线程处于Sleep状态,等待Vsync信号Vsync信号到来,......
  • ‌AI、AO、DI、DO的解释
    ‌AI、AO、DI、DO在自动化和控制系统中的含义如下‌:‌ ‌AI(AnalogInput)‌:模拟量输入。AI代表将现实世界中的模拟信号(如温度、压力、流量等)转换成计算机能处理的数字信号。例如,温度传感器输出的电压或电流信号被转换为数字信号以便计算机处理。‌AO(AnalogOutput)‌:模拟量输出......
  • Python 融合豆包 AI 优化测试用例生成
    在软件开发过程中,测试用例的编写是确保软件质量的关键环节。传统的测试用例编写往往依赖人工经验,不仅耗时费力,还容易出现遗漏。随着人工智能技术的发展,利用AI来辅助生成测试用例成为了提高效率和质量的有效途径。本文将介绍如何使用Python与豆包AI融合,实现测试用例的......
  • springboot助农管理系统 毕业设计源码15080
                                 目录1绪论1.1研究背景及意义1.3系统开发的目标意义1.4论文结构与章节安排2.助农管理系统系统分析2.1可行性分析2.2系统功能分析2.3 系统用例分析2.4业务流程......