Spring Boot 无缝集成SpringAI的函数调用模块
这是一个 完整的 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 "北京现在的天气怎么样?"
模型响应流程:
- 模型识别需要调用
getCurrentWeather(location="北京")
。 - Spring AI 自动触发
WeatherService.getWeather("北京")
。 - 函数返回
"晴,气温 25°C"
。 - 模型生成最终回答:
"北京当前的天气是晴,气温 25°C。"
测试请求 2:需要澄清参数
curl -X POST http://localhost:8080/chat -d "帮我查一下天气"
模型响应:
请问您要查询哪个城市的天气?
6. 关键代码解析
6.1 函数元数据的重要性
@Function
注解:提供模型理解函数用途的关键信息,影响模型是否决定调用。- 参数描述:清晰的参数描述(如
location
类型为城市名称)提升模型参数提取准确性。
6.2 函数执行流程
- 模型决策:根据用户输入,模型决定是否调用函数。
- 参数解析:
WeatherRequestConverter
将模型传入的 JSON 参数转为 Java 类型。 - 自动执行:Spring AI 自动调用注册的
WeatherService.getWeather()
方法。 - 结果回传:函数返回结果自动注入后续对话上下文,模型生成最终回答。
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. 调试技巧
- 查看元数据:检查
response.getMetadata()
中的function_call.*
字段。 - 日志拦截:添加
Advisor
记录函数调用请求和响应。 - 模拟测试:使用 Mock 替换真实函数实现,验证参数传递逻辑。
将函数调用无缝集成到 Spring Boot 应用以后,即可实现动态数据获取与业务逻辑触发。如需进一步优化(如异步执行函数),可结合 @Async
或消息队列扩展。