Spring Boot 无缝集成SpringAI的函数调用模块

news2025/1/31 17:38:51

这是一个 完整的 Spring AI 函数调用实例,涵盖从函数定义、注册到实际调用的全流程,以「天气查询」功能为例,结合代码详细说明:


1. 环境准备

1.1 添加依赖
<!-- Spring AI OpenAI -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
1.2 配置 OpenAI 密钥
# application.properties
spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.options.model=gpt-3.5-turbo-0125

2. 定义函数逻辑

2.1 天气服务接口
@Service
public class WeatherService {

    // 模拟天气数据存储
    private Map<String, String> weatherData = Map.of(
        "北京", "晴,气温 25°C",
        "上海", "多云,气温 28°C",
        "广州", "阵雨,气温 30°C"
    );

    /**
     * 定义函数:获取当前天气
     * @Function 注解描述函数元数据
     */
    @Function(
        name = "getCurrentWeather",
        description = "获取指定城市的当前天气信息",
        inputType = @Function.Parameter(
            type = "object",
            properties = @Function.ParameterProperty(
                name = "location", 
                type = "string", 
                description = "城市名称,如 '北京'"
            )
        )
    )
    public String getWeather(@RequestParam String location) {
        return weatherData.getOrDefault(location, "暂无该城市天气数据");
    }
}

3. 注册函数到 Spring AI

3.1 函数回调配置
@Configuration
public class FunctionConfig {

    @Bean
    public FunctionCallback weatherFunction(WeatherService weatherService) {
        return new FunctionCallbackWrapper<>(
            "getCurrentWeather", // 函数名称(必须与 @Function 注解一致)
            "获取天气信息",        // 函数描述(可选)
            weatherService::getWeather, // 函数实现方法引用
            new WeatherRequestConverter() // 参数转换器(见下一步)
        );
    }

    // 参数转换器:将模型传入的 JSON 参数转换为 Java 对象
    private static class WeatherRequestConverter implements Converter<String, String> {
        @Override
        public String convert(String source) {
            // 解析 JSON 参数(示例简化,实际可使用 Jackson)
            return source.replaceAll("\"", "").split(":")[1].trim();
        }
    }
}
3.2 启用函数调用
@Configuration
public class ChatConfig {

    @Bean
    public ChatClient chatClient(
        OpenAiChatClient chatClient,
        List<FunctionCallback> functionCallbacks
    ) {
        // 将函数回调注册到 ChatClient
        chatClient.setFunctionCallbacks(functionCallbacks);
        return chatClient;
    }
}

4. 实现对话接口

@RestController
public class ChatController {

    @Autowired
    private ChatClient chatClient;

    @PostMapping("/chat")
    public String chat(@RequestBody String userMessage) {
        // 构造对话请求
        UserMessage message = new UserMessage(
            userMessage,
            OpenAiChatOptions.builder()
                .withFunctionCallbacks(List.of("getCurrentWeather")) // 允许调用的函数
                .build()
        );

        // 发送请求并获取响应
        ChatResponse response = chatClient.call(message);
        
        // 处理可能的函数调用结果
        if (response.getMetadata().containsKey("function_call")) {
            return handleFunctionCall(response);
        }
        
        return response.getResult().getOutput().getContent();
    }

    private String handleFunctionCall(ChatResponse response) {
        // 解析函数调用请求
        String functionName = response.getMetadata().get("function_call.name").toString();
        String functionArgs = response.getMetadata().get("function_call.arguments").toString();

        // 执行函数(此处实际由 Spring AI 自动处理,此处仅为演示)
        String result = "执行函数 " + functionName + " 参数: " + functionArgs;
        
        // 将结果回传模型生成最终回答
        ChatResponse finalResponse = chatClient.call(
            new UserMessage("函数执行结果:" + result)
        );
        return finalResponse.getResult().getOutput().getContent();
    }
}

5. 完整流程测试

测试请求 1:直接提问
curl -X POST http://localhost:8080/chat -H "Content-Type: text/plain" -d "北京现在的天气怎么样?"

模型响应流程

  1. 模型识别需要调用 getCurrentWeather(location="北京")
  2. Spring AI 自动触发 WeatherService.getWeather("北京")
  3. 函数返回 "晴,气温 25°C"
  4. 模型生成最终回答:"北京当前的天气是晴,气温 25°C。"
测试请求 2:需要澄清参数
curl -X POST http://localhost:8080/chat -d "帮我查一下天气"

模型响应

请问您要查询哪个城市的天气?

6. 关键代码解析

6.1 函数元数据的重要性
  • @Function 注解:提供模型理解函数用途的关键信息,影响模型是否决定调用。
  • 参数描述:清晰的参数描述(如 location 类型为城市名称)提升模型参数提取准确性。
6.2 函数执行流程
  1. 模型决策:根据用户输入,模型决定是否调用函数。
  2. 参数解析WeatherRequestConverter 将模型传入的 JSON 参数转为 Java 类型。
  3. 自动执行:Spring AI 自动调用注册的 WeatherService.getWeather() 方法。
  4. 结果回传:函数返回结果自动注入后续对话上下文,模型生成最终回答。

7. 扩展场景

7.1 多函数协同

定义更多函数并注册:

// 股票查询函数
@Function(name = "getStockPrice", description = "查询股票实时价格")
public String getStockPrice(@RequestParam String symbol) { ... }

// 注册
@Bean
public FunctionCallback stockFunction(StockService stockService) { ... }
7.2 动态函数调用列表

根据用户身份动态启用不同函数:

UserMessage message = new UserMessage(
    input,
    OpenAiChatOptions.builder()
        .withFunctionCallbacks(getAllowedFunctions(userRole)) // 根据角色返回允许的函数列表
        .build()
);

8. 调试技巧

  1. 查看元数据:检查 response.getMetadata() 中的 function_call.* 字段。
  2. 日志拦截:添加 Advisor 记录函数调用请求和响应。
  3. 模拟测试:使用 Mock 替换真实函数实现,验证参数传递逻辑。

将函数调用无缝集成到 Spring Boot 应用以后,即可实现动态数据获取与业务逻辑触发。如需进一步优化(如异步执行函数),可结合 @Async 或消息队列扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2288007.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MQ】探索 Kafka

高性能 消息的顺序性、顺序写磁盘 零拷贝 RocketMQ内部主要是使用基于mmap实现的零拷贝&#xff0c;用来读写文件 减少cpu的拷贝次数和上下文切换次数&#xff0c;实现文件的高效读写操作 Kafka 零拷贝 Kafka 使用到了 mmap 和 sendfile 的方式来实现零拷贝。分别对应 Jav…

指针(C语言)从0到1掌握指针,为后续学习c++打下基础

目录 一&#xff0c;指针 二&#xff0c;内存地址和指针 1&#xff0c;什么是内存地址 2&#xff0c;指针在不同系统下所占内存 三&#xff0c;指针的声明和初始化以及类型 1,指针的声明 2,指针 的初始化 1&#xff0c; 初始化方式优点及适用场景 4,指针的声明初始化类型…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(九)(完结篇)

文章目录 一、成绩查询模块实现1、学生成绩查询功能实现1.1 页面设计1.2 前端页面实现1.3 后端功能实现2、成绩分段查询功能实现2.1 页面设计2.2 前端页面实现2.3 后端功能实现二、试卷练习模块实现三、我的分数模块实现1、 页面设计2、 前端页面实现3、 后端功能实现四、交流区…

AI DeepSeek-R1 Windos 10 环境搭建

1、安装&#xff1a; 下载 Python |Python.org CUDA Drivers for MAC Archive | NVIDIA pip 和virtualenv Download Ollama on Windows 如下图 2、下载模型 deepseek-r1 ollama run deepseek-r1 或者可以ollama run deepseek-r1:8b 或 3、安装一个可视化对话Chatbox 下载 …

【懒删除堆】力扣2349. 设计数字容器系统

设计一个数字容器系统&#xff0c;可以实现以下功能&#xff1a; 在系统中给定下标处 插入 或者 替换 一个数字。 返回 系统中给定数字的最小下标。 请你实现一个 NumberContainers 类&#xff1a; NumberContainers() 初始化数字容器系统。 void change(int index, int numb…

【Block总结】OutlookAttention注意力,捕捉细节和局部特征|即插即用

论文信息 标题: VOLO: Vision Outlooker for Visual Recognition作者: Li Yuan, Qibin Hou, Zihang Jiang, Jiashi Feng, Shuicheng Yan代码链接: https://github.com/sail-sg/volo论文链接: https://arxiv.org/pdf/2106.13112 创新点 前景注意力机制: VOLO引入了一种称为“…

有效运作神经网络

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 训练集、验证集、测试集偏差、方差正则化正则化参数为什么正则化可以减少过拟合Dropout正则化Inverted Dropout其他的正则化方法数据增广Early stopping 归一化梯度消失与梯度爆…

Vue 组件开发:构建高效可复用的前端界面要素

1 引言 在现代 Web 开发中,构建高效且可复用的前端界面要素是提升开发效率和用户体验的关键。Vue.js 作为一种轻量级且功能强大的前端框架,提供了丰富的工具和机制,帮助开发者快速构建高质量的应用程序。通过合理设计和封装 Vue 组件,我们可以实现组件的高效复用,提高开发…

Spring Boot + Facade Pattern : 通过统一接口简化多模块业务

文章目录 Pre概述在编程中&#xff0c;外观模式是如何工作的&#xff1f;外观设计模式 UML 类图外观类和子系统的关系优点案例外观模式在复杂业务中的应用实战运用1. 项目搭建与基础配置2. 构建子系统组件航班服务酒店服务旅游套餐服务 3. 创建外观类4. 在 Controller 中使用外…

OpenAI Operator:AI Agent 大战的号角,从 “工具” 到 “助手” 的飞跃

想尝试不同的 AI 模型&#xff1f;不必到处寻找&#xff01;chatTools 为您集成了 o1、GPT4o、Claude 和 Gemini 等多种选择&#xff0c;一个平台解决您的所有 AI 需求。现在就来体验吧&#xff01; 各位 AI 爱好者们&#xff0c;今天我们来聊聊 OpenAI 的最新力作——Operator…

AI大模型开发原理篇-9:GPT模型的概念和基本结构

基本概念 生成式预训练模型 GPT&#xff08;Generative Pre-trained Transformer&#xff09;模型 是由 OpenAI 开发的基于 Transformer 架构的自然语言处理&#xff08;NLP&#xff09;模型&#xff0c;专门用于文本生成任务。它的设计理念在于通过大规模的预训练来学习语言模…

Java Swing 基础组件详解 [论文投稿-第四届智能系统、通信与计算机网络]

大会官网&#xff1a;www.icisccn.net Java Swing 是一个功能强大的 GUI 工具包&#xff0c;提供了丰富的组件库用于构建跨平台的桌面应用程序。本文将详细讲解 Swing 的基础组件&#xff0c;包括其作用、使用方法以及示例代码&#xff0c;帮助你快速掌握 Swing 的核心知识。 一…

vscode+WSL2(ubuntu22.04)+pytorch+conda+cuda+cudnn安装系列

最近在家过年闲的没事&#xff0c;于是研究起深度学习开发工具链的配置和安装&#xff0c;之前欲与天公试比高&#xff0c;尝试在win上用vscodecuda11.6vs2019的cl编译器搭建cuda c编程环境&#xff0c;最后惨败&#xff0c;沦为笑柄&#xff0c;痛定思痛&#xff0c;这次直接和…

想品客老师的第九天:原型和继承

原型与继承前置看这里 原型 原型都了解了&#xff0c;但是不是所有对象都有对象原型 let obj1 {}console.log(obj1)let obj2 Object.create(null, {name: {value: 荷叶饭}})console.log(obj2) obj2为什么没有对象原型&#xff1f;obj2是完全的数据字典对象&#xff0c;没有…

Time Constant | RC、RL 和 RLC 电路中的时间常数

注&#xff1a;本文为 “Time Constant” 相关文章合辑。 机翻&#xff0c;未校。 How To Find The Time Constant in RC and RL Circuits June 8, 2024 &#x1f4a1; Key learnings: 关键学习点&#xff1a; Time Constant Definition: The time constant (τ) is define…

芯片AI深度实战:实战篇之vim chat

利用vim-ollama这个vim插件&#xff0c;可以在vim内和本地大模型聊天。 系列文章&#xff1a; 芯片AI深度实战&#xff1a;基础篇之Ollama-CSDN博客 芯片AI深度实战&#xff1a;基础篇之langchain-CSDN博客 芯片AI深度实战&#xff1a;实战篇之vim chat-CSDN博客 芯片AI深度…

Spring Boot 日志:项目的“行车记录仪”

一、什么是Spring Boot日志 &#xff08;一&#xff09;日志引入 在正式介绍日志之前&#xff0c;我们先来看看上篇文章中&#xff08;Spring Boot 配置文件&#xff09;中的验证码功能的一个代码片段&#xff1a; 这是一段校验用户输入的验证码是否正确的后端代码&#xff0c…

【QT】 控件 -- 显示类

&#x1f525; 目录 [TOC]( &#x1f525; 目录) 1. 前言 2. 显示类控件2.1 Label 1、显示不同文本2、显示图片3、文本对齐、自动换行、缩进、边距4、设置伙伴 3.2 LCD Number 3.3 ProgressBar 3.4 Calendar Widget 3. 共勉 &#x1f525; 1. 前言 之前我在上一篇文章【QT】…

冲刺蓝桥杯之速通vector!!!!!

文章目录 知识点创建增删查改 习题1习题2习题3习题4&#xff1a;习题5&#xff1a; 知识点 C的STL提供已经封装好的容器vector&#xff0c;也可叫做可变长的数组&#xff0c;vector底层就是自动扩容的顺序表&#xff0c;其中的增删查改已经封装好 创建 const int N30; vecto…

小米CR6606,CR6608,CR6609 启用SSH和刷入OpenWRT 23.05.5

闲鱼上收了一台CR6606和一台CR6609, 一直没时间研究, 趁春节假期把这两个都刷成 OpenWRT 配置说明 CPU: MT7621AT&#xff0c;双核880MHz内存: NT5CC128M16JR-EKI 或 M15T2G16128A, 256MB闪存: F59L1G81MB, 128MB无线基带芯片(BB): T7905DAN无线射频芯片(RF): MT7975DN无外置F…